Це приємна вправа для того, щоб стати більш вільним на тій мові програмування, яку ви хотіли вивчити, але лише злегка познайомилися. Це передбачає роботу з об'єктами, використання або моделювання закриттів та розтягування системи типу.
Ваше завдання - написати код для управління ледачими списками, а потім використовувати його для реалізації цього алгоритму для генерації чисел Фібоначчі:
Зразки коду є в Haskell
let fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
in take 40 fibs
Результат:
[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]
Ваша реалізація списку лінивих повинна відповідати цим рекомендаціям:
- Вузол списку - це одна з трьох речей:
- Nil - Порожній список.
[]
- Мінуси - один елемент, сполучений зі списком решти елементів:
1 : [2,3,4,5]
(:
є оператором мінусів у Haskell) - Thunk - відкладене обчислення, яке створює вузол List при необхідності.
- Nil - Порожній список.
- Він підтримує такі операції:
- nil - побудова порожнього списку.
- мінуси - Побудова клітинки проти.
- thunk - Побудуйте Thunk, задавши функцію, яка не бере аргументів і повертає Nil або мінуси.
- сила - Дано вузол списку:
- Якщо це ніль або мінуси, просто поверніть його.
- Якщо це Thunk, виклик його функції, щоб отримати нуль або мінуси. Замініть тулуб на той Ніл або Мінуси і поверніть його.
Примітка: Заміна грона примусовим значенням - важлива частина визначення поняття "ледачий" . Якщо цей крок буде пропущений, алгоритм Фібоначчі вище буде надто повільним.
- порожній - подивіться, чи вузол списку є Nil (після вимушення).
- head (він же "машина") - Отримайте перший пункт списку (або киньте форму, якщо це Nil).
- хвіст (він же "cdr") - Отримайте елементи після заголовка списку (або киньте форму, якщо це Nil).
- zipWith - З огляду на бінарну функцію (наприклад
(+)
) та два (можливо, нескінченні) списки, застосуйте цю функцію до відповідних елементів списків. Приклад:
zipWith (+) [1,2,3] [1,1,10] == [2,3,13]
- take - З огляду на число N та (можливо, нескінченний) список, візьміть перші N елементів списку.
- друк - друк усіх елементів у списку. Це повинно працювати поступово, якщо їм надано довгий або нескінченний список.
fibs
використовує себе у власному визначенні. Налаштування ледачої рекурсії - це складність; вам потрібно зробити щось подібне:- Виділіть грудку для
fibs
. Залиште це поки в манекеновому стані. - Визначте функцію грона, яка залежить від посилання на
fibs
. - Оновіть грудку за допомогою її функції.
Ви можете заховати цю сантехніку, визначивши функцію,
fix
яка викликає функцію повернення списку зі своїм власним значенням повернення. Розгляньте короткий сон, щоб ця ідея могла втілитися.- Виділіть грудку для
Поліморфізм (вміння працювати зі списками будь-якого виду предметів) не потрібен, але подивіться, чи зможете ви знайти мову, яка ідіоматична для вашої мови.
- Не турбуйтеся про управління пам'яттю. Навіть мови зі збиранням сміття мають тенденцію переносити об’єкти, які ви ніколи більше не використовуватимете (наприклад, у стеку дзвінків), тому не дивуйтеся, якщо ваша програма просочує пам'ять під час проходження нескінченного списку.
Не соромтесь трохи відхилятися від цих вказівок, щоб вмістити деталі вашої мови або вивчити альтернативні підходи до лінивих списків.
Правила:
- Оберіть мову, яку ви не добре знаєте. Я не можу "вимагати" цього, звідси і тег "система честі". Однак виборці можуть перевірити вашу історію, щоб побачити, на яких мовах ви публікували публікації.
Не використовуйте вбудовану підтримку лінивого списку вашої мови, щоб зробити все. Опублікуйте щось істотне або принаймні цікаве.
Haskell майже не виходить. Тобто, якщо ви не зробите щось подібне:
data List a = IORef (ListNode a) data ListNode a = Nil | Cons !a !(List a) | Thunk !(IO (ListNode a))
Примітка. Неточна оцінка Haskell не є обмеженою, але реалізація вашого лінивого списку не повинна виводити його звідси безпосередньо. Насправді, було б цікаво побачити ефективне, суто функціональне рішення, яке не потребує ліні.
Пітон:
- Не використовуйте itertools.
- Генератори в порядку, але ви їх використовуєте, вам доведеться знайти спосіб запам’ятовування вимушених значень.
zipWith (+) [1,2,3,4,5] [0,0,0] == [1,2,3]
. Однак це не має значення для вищезгаданого алгоритму Фібоначчі, оскільки обидва аргументи zipWith є нескінченними списками.
fibs
правильно здійснити , як це залежить від самого себе. Я оновив питання, щоб детальніше розглянути питання про ледачу рекурсію. FUZxxl розібрав це сам / її / сам.
zipWith
у два списки різної довжини?