Это хорошее упражнение для того, чтобы стать более свободно говорящим на том языке программирования, который вы хотели выучить, но с которым легко повозились. Это включает в себя работу с объектами, использование или моделирование замыканий и растяжение системы типов.
Ваша задача - написать код для управления отложенными списками, а затем использовать его для реализации этого алгоритма генерации чисел Фибоначчи:
Примеры кода в Хаскеле
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]
Ваша реализация отложенного списка должна соответствовать этим рекомендациям:
- Узел List - это одна из трех вещей:
- Ноль - пустой список.
[]
- Минусы - один элемент в сочетании со списком оставшихся элементов:
1 : [2,3,4,5]
(:
является оператором cons в Haskell) - Thunk - отложенное вычисление, которое при необходимости создает узел List.
- Ноль - пустой список.
- Поддерживаются следующие операции:
- nil - создать пустой список
- минусы - построить против клетки.
- thunk - Создайте Thunk, учитывая функцию, которая не принимает аргументов и возвращает Nil или Cons.
- force - задан узел List:
- Если это ноль или минусы, просто верните его.
- Если это Thunk, вызовите его функцию, чтобы получить ноль или минусы. Замените громоотвод с этим ноль или минусы и верните его.
Примечание. Замена thunk его принудительным значением является важной частью определения «ленивый» . Если этот шаг пропущен, приведенный выше алгоритм Фибоначчи будет слишком медленным.
- empty - посмотреть, является ли узел List нулем (после его форсирования).
- head (он же «машина») - Получить первый элемент списка (или бросить приступ, если это ноль).
- tail (он же "cdr") - получает элементы после заголовка списка (или подбрасывает, если это Nil).
- zipWith - учитывая двоичную функцию (например
(+)
) и два (возможно, бесконечные) списка, примените функцию к соответствующим элементам списков. Пример:
zipWith (+) [1,2,3] [1,1,10] == [2,3,13]
- take - Учитывая число N и (возможно, бесконечный) список, возьмите первые N элементов списка.
- печать - печать всех элементов в списке. Это должно работать постепенно, когда дан длинный или бесконечный список.
fibs
использует себя в своем собственном определении. Настроить ленивую рекурсию немного сложно; вам нужно сделать что-то вроде этого:- Выделите громоотвод для
fibs
. Оставьте это в фиктивном состоянии пока. - Определите функцию thunk, которая зависит от ссылки на
fibs
. - Обновите Thunk с его функцией.
Возможно, вы захотите скрыть это, определив функцию,
fix
которая вызывает функцию, возвращающую список, со своим собственным возвращаемым значением. Подумайте о том, чтобы немного вздремнуть, чтобы эта идея начала развиваться- Выделите громоотвод для
Полиморфизм (способность работать со списками элементов любого типа) не требуется, но посмотрите, можете ли вы найти способ сделать это, что идиоматично для вашего языка.
- Не беспокойтесь об управлении памятью. Даже языки со сборкой мусора имеют тенденцию переносить объекты, которые вы никогда больше не будете использовать (например, в стеке вызовов), поэтому не удивляйтесь, если ваша программа утечет память при обходе бесконечного списка.
Не стесняйтесь слегка отклониться от этих рекомендаций, чтобы учесть особенности вашего языка или изучить альтернативные подходы к ленивым спискам.
Правила:
- Выберите язык, который вы плохо знаете. Я не могу «требовать» этого, отсюда и тэг «honor-system». Тем не менее, избиратели могут проверить вашу историю, чтобы увидеть, на каких языках вы публиковали.
Не используйте встроенную поддержку ленивых списков вашего языка, чтобы делать все. Опубликовать что-то существенное или хотя бы интересное.
Хаскелл в значительной степени отсутствует. То есть, если вы не сделаете что-то вроде этого:
data List a = IORef (ListNode a) data ListNode a = Nil | Cons !a !(List a) | Thunk !(IO (ListNode a))
Примечание: нестрогая оценка Haskell не является запретной, но ваша реализация отложенного списка не должна получать свои возможности непосредственно оттуда. На самом деле, было бы интересно увидеть эффективное, чисто функциональное решение, которое не требует лени.
Python:
- Не используйте itertools.
- Генераторы в порядке, но вы используете их, вам придется найти способ запоминать принудительные значения.
zipWith (+) [1,2,3,4,5] [0,0,0] == [1,2,3]
. Однако это не имеет значения для алгоритма Фибоначчи, приведенного выше, поскольку оба аргумента zipWith представляют собой бесконечные списки.
fibs
правильной реализации , так как это зависит от самого себя. Я обновил вопрос, чтобы развить ленивую рекурсию. FUZxxl понял это сам.
zipWith
двух списков разной длины?