Đây là một bài tập hay để trở nên thông thạo ngôn ngữ lập trình mà bạn muốn học, nhưng chỉ biết mày mò một cách nhẹ nhàng. Điều này liên quan đến việc làm việc với các đối tượng, sử dụng hoặc mô phỏng các lần đóng và kéo dài hệ thống loại.
Nhiệm vụ của bạn là viết mã để quản lý các danh sách lười biếng, sau đó sử dụng nó để thực hiện thuật toán này để tạo các số Fibonacci:
Mẫu mã có trong Haskell
let fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
in take 40 fibs
Kết quả:
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,39088169,63245986]
Việc thực hiện danh sách lười biếng của bạn phải đáp ứng các nguyên tắc sau:
- Nút List là một trong ba điều sau:
- Nil - Danh sách trống.
[]
- Nhược điểm - Một mục duy nhất, được ghép nối với Danh sách các mục còn lại:
1 : [2,3,4,5]
(:
là toán tử khuyết điểm trong Haskell) - Thunk - Một tính toán hoãn lại tạo ra một nút List khi cần thiết.
- Nil - Danh sách trống.
- Nó hỗ trợ các hoạt động sau:
- nil - Xây dựng một danh sách trống.
- Nhược điểm - Xây dựng một tế bào khuyết điểm.
- thunk - Xây dựng Thunk, được cung cấp một hàm không có đối số và trả về Nil hoặc Cons.
- lực lượng - Cho một nút Danh sách:
- Nếu đó là Nil hoặc Cons, chỉ cần trả lại.
- Nếu đó là Thunk, hãy gọi chức năng của nó để nhận Nil hoặc Cons. Thay thunk bằng Nil hoặc Cons đó và trả lại.
Lưu ý: Thay thế thunk bằng giá trị bắt buộc của nó là một phần quan trọng trong định nghĩa "lười biếng" . Nếu bước này bị bỏ qua, thuật toán Fibonacci ở trên sẽ quá chậm.
- trống rỗng - Xem nếu một nút Danh sách là Nil (sau khi buộc nó).
- head (hay còn gọi là "xe hơi") - Nhận mục đầu tiên của danh sách (hoặc ném phù hợp nếu đó là Nil).
- đuôi (còn gọi là "cdr") - Nhận các phần tử sau phần đầu của danh sách (hoặc ném phù hợp nếu nó không).
- zipWith - Đưa ra một hàm nhị phân (ví dụ
(+)
) và hai danh sách (có thể là vô hạn), áp dụng hàm cho các mục tương ứng của danh sách. Thí dụ:
zipWith (+) [1,2,3] [1,1,10] == [2,3,13]
- mất - Cho một số N và một danh sách (có thể là vô hạn), lấy N mục đầu tiên của danh sách.
- in - In tất cả các mục trong một danh sách. Điều này sẽ làm việc tăng dần khi được đưa ra một danh sách dài hoặc vô hạn.
fibs
sử dụng chính nó trong định nghĩa riêng của nó. Thiết lập đệ quy lười biếng là một mẹo khó khăn; bạn sẽ cần phải làm một cái gì đó như thế này:- Phân bổ một thunk cho
fibs
. Để nó trong một trạng thái giả bây giờ. - Xác định hàm thunk, phụ thuộc vào tham chiếu đến
fibs
. - Cập nhật thunk với chức năng của nó.
Bạn có thể muốn ẩn hệ thống ống nước này bằng cách xác định hàm
fix
gọi hàm trả về Danh sách với giá trị trả về của chính nó. Cân nhắc ngủ trưa ngắn để ý tưởng này có thể được thiết lập.- Phân bổ một thunk cho
Không đa hình (khả năng làm việc với danh sách của bất kỳ loại mặt hàng nào) là không bắt buộc, nhưng hãy xem liệu bạn có thể tìm ra cách để làm điều đó thành ngữ trong ngôn ngữ của bạn không.
- Đừng lo lắng về việc quản lý bộ nhớ. Ngay cả các ngôn ngữ có bộ sưu tập rác cũng có xu hướng mang theo các vật thể bạn sẽ không bao giờ sử dụng lại (ví dụ: trên ngăn xếp cuộc gọi), vì vậy đừng ngạc nhiên nếu chương trình của bạn bị rò rỉ bộ nhớ trong khi duyệt qua danh sách vô hạn.
Vui lòng đi chệch một chút so với các hướng dẫn này để phù hợp với các chi tiết của ngôn ngữ của bạn hoặc để khám phá các phương pháp tiếp cận thay thế cho danh sách lười biếng.
Quy tắc:
- Chọn một ngôn ngữ bạn không biết rõ. Tôi không thể "yêu cầu" cái này, do đó thẻ "hệ thống danh dự". Tuy nhiên, cử tri có thể kiểm tra lịch sử của bạn để xem những ngôn ngữ bạn đã đăng.
Đừng sử dụng hỗ trợ danh sách lười biếng tích hợp sẵn trong ngôn ngữ của bạn để làm mọi thứ. Đăng một cái gì đó đáng kể hoặc ít nhất là thú vị.
Haskell là khá nhiều ra. Đó là, trừ khi bạn làm một cái gì đó như thế này:
data List a = IORef (ListNode a) data ListNode a = Nil | Cons !a !(List a) | Thunk !(IO (ListNode a))
Lưu ý: Đánh giá không nghiêm ngặt của Haskell không vượt quá giới hạn, nhưng việc thực hiện danh sách lười biếng của bạn không nên lấy khả năng của nó trực tiếp từ đó. Trên thực tế, thật thú vị khi thấy một giải pháp hiệu quả, hoàn toàn chức năng không đòi hỏi sự lười biếng.
Con trăn:
- Đừng sử dụng itertools.
- Máy phát điện vẫn ổn, nhưng bạn sử dụng chúng, bạn sẽ phải tìm cách ghi nhớ các giá trị bắt buộc.
zipWith (+) [1,2,3,4,5] [0,0,0] == [1,2,3]
. Tuy nhiên, điều này không quan trọng đối với thuật toán Fibonacci ở trên, vì cả hai đối số cho zipWith đều là danh sách vô hạn.
fibs
chính xác, vì nó phụ thuộc vào chính nó. Tôi cập nhật câu hỏi để giải thích về đệ quy lười biếng. FUZxxl đã tìm ra nó bởi anh ấy / cô ấy / cô ấy.
zipWith
hai danh sách có độ dài khác nhau?