TG Telegram Group & Channel
Scala bin | United States America (US)
Create: Update:

Одним из наиболее важных качеств функциональных языков является их лаконичность и выразительность, что позволяет сконцентрироваться на решении задачи, а не на синтаксисе языка.
Рассмотрим, к примеру, классическую задачу получения n первых чисел в последовательности Фибоначчи и вывода их на экран.
(Важно: в задаче не учитываются ограничения на размер численных типов данных - на самом деле даже long исчерпывается достаточно быстро (~92 число Фибоначчи)).

Вариант №1 - императивный (Java <6).
Всё просто - классическое создание массива необходимого размера и его последующее заполнение. При этом требуется дополнительная логика проверки входных значений.
Печать в консоль также производится с помощью итерации по элементам массива.
Примечательно, что Java позволяет создавать массивы нулевой длины, так что этого типа данных достаточно для выполнения данной задачи.

https://gist.github.com/J0kerPanda/8ff3475b10bdc4e7a986322daf94a90c

Вариант №2 - рекурсивный (Java 8).
Перепишем данную функцию с использованием рекурсии. Поскольку для итераций необходим аккуммулятор значений, необходимо
вынести рекурсивную логику в отдельную вспомогательную функцию. В связи с тем, что Java не позволяет объявлять вложенные функции,
её необходимо объявить на том же уровне, что и основную, что затрудняет чтение кода. Сама функция, несмотря на рекурсивную природу,
слабо отличается от предыдущей в виду аналогичной логики, построенной на if, else. При этом, Java не оптимизирует хвостовую рекурсию,
поэтому при больших n вызов данной функции приведёт к переполнению стэка, чего не случилось бы в случае с циклом.
Стоит также отметить, что данная функция возвращает список в обратном порядке, поэтому, при необходимости, его необходимо инвертировать.
Для печати используется метод класса List - forEach, применяющий функцию System.out::println, переданную в качестве аргумента, к каждому значению в списке.

https://gist.github.com/J0kerPanda/c0057ef5d9c823f47ef068a822946f67

Вариант №3 - рекурсивный (Scala).
В данном случае, для непосредственного вычисления списка чисел Фибоначчи используется внутренняя функция inner, полностью зависящая только от своих аргументов.
Она хорошо отражает рекурсивную структуру базового алгоритма, а вставка начальных значений органично объединяется с основным случаем при помощи pattern-матчинга.
В данном случае вспомогательная функция изолирована от остального кода. При этом, поскольку Scala оптимизирует хвосторекурсивные вызовы, эффективность работы
данной функции сопоставима с циклом.
Числа Фибоначчи, как и в предыдущем случае, следуют в обратном порядке, а вывод также использует метод foreach.

https://gist.github.com/J0kerPanda/8e965ef34003ce3bcb23da98f27d8adc

Вариант №4 - потоковый (Scala)

Предыдущие примеры были написаны целиком с использованием функций для генерации списков чисел Фибоначчи. Однако, если вспомнить о математической природе задачи,
эти числа представляют собой бесконечную последовательность, подчинённую определённому закону. Естественным способом реализации бесконечных последовательностей
в функциональном программировании являются потоки (Stream).
Если представить себе поток пар чисел Фиббоначи (числа в паре упорядочены по возрастанию), то каждая последующая пара может быть получена из предыдущей из
наибольшего элемента предыдущей пары и суммы элементов предыдущей пары. Это, в свою очередь, позволяет получать необходимый список n чисел Фибоначчи как взятие n
элементов данного потока с последующей декомпозицией каждой пары в свой наибольший элемент. Код для получения такого потока занимает одну строчку.

val s  = 1 #:: Stream.iterate((1, 1)) { case (n1, n2) => (n2, n1 + n2) }.map(_._2)

...

s.take(n).foreach(println)

Вариант №5 - потоковый (Haskell)

Можно ли ещё короче? Haskell позволяет уложиться в 27 символов, но этот код менее понятен, чем предыдущий вариант, и требует понимания работы как библиотечной функции scanl, так и самого Haskell.

fibs = 1 : scanl (+) 1 fibs

Одним из наиболее важных качеств функциональных языков является их лаконичность и выразительность, что позволяет сконцентрироваться на решении задачи, а не на синтаксисе языка.
Рассмотрим, к примеру, классическую задачу получения n первых чисел в последовательности Фибоначчи и вывода их на экран.
(Важно: в задаче не учитываются ограничения на размер численных типов данных - на самом деле даже long исчерпывается достаточно быстро (~92 число Фибоначчи)).

Вариант №1 - императивный (Java <6).
Всё просто - классическое создание массива необходимого размера и его последующее заполнение. При этом требуется дополнительная логика проверки входных значений.
Печать в консоль также производится с помощью итерации по элементам массива.
Примечательно, что Java позволяет создавать массивы нулевой длины, так что этого типа данных достаточно для выполнения данной задачи.

https://gist.github.com/J0kerPanda/8ff3475b10bdc4e7a986322daf94a90c

Вариант №2 - рекурсивный (Java 8).
Перепишем данную функцию с использованием рекурсии. Поскольку для итераций необходим аккуммулятор значений, необходимо
вынести рекурсивную логику в отдельную вспомогательную функцию. В связи с тем, что Java не позволяет объявлять вложенные функции,
её необходимо объявить на том же уровне, что и основную, что затрудняет чтение кода. Сама функция, несмотря на рекурсивную природу,
слабо отличается от предыдущей в виду аналогичной логики, построенной на if, else. При этом, Java не оптимизирует хвостовую рекурсию,
поэтому при больших n вызов данной функции приведёт к переполнению стэка, чего не случилось бы в случае с циклом.
Стоит также отметить, что данная функция возвращает список в обратном порядке, поэтому, при необходимости, его необходимо инвертировать.
Для печати используется метод класса List - forEach, применяющий функцию System.out::println, переданную в качестве аргумента, к каждому значению в списке.

https://gist.github.com/J0kerPanda/c0057ef5d9c823f47ef068a822946f67

Вариант №3 - рекурсивный (Scala).
В данном случае, для непосредственного вычисления списка чисел Фибоначчи используется внутренняя функция inner, полностью зависящая только от своих аргументов.
Она хорошо отражает рекурсивную структуру базового алгоритма, а вставка начальных значений органично объединяется с основным случаем при помощи pattern-матчинга.
В данном случае вспомогательная функция изолирована от остального кода. При этом, поскольку Scala оптимизирует хвосторекурсивные вызовы, эффективность работы
данной функции сопоставима с циклом.
Числа Фибоначчи, как и в предыдущем случае, следуют в обратном порядке, а вывод также использует метод foreach.

https://gist.github.com/J0kerPanda/8e965ef34003ce3bcb23da98f27d8adc

Вариант №4 - потоковый (Scala)

Предыдущие примеры были написаны целиком с использованием функций для генерации списков чисел Фибоначчи. Однако, если вспомнить о математической природе задачи,
эти числа представляют собой бесконечную последовательность, подчинённую определённому закону. Естественным способом реализации бесконечных последовательностей
в функциональном программировании являются потоки (Stream).
Если представить себе поток пар чисел Фиббоначи (числа в паре упорядочены по возрастанию), то каждая последующая пара может быть получена из предыдущей из
наибольшего элемента предыдущей пары и суммы элементов предыдущей пары. Это, в свою очередь, позволяет получать необходимый список n чисел Фибоначчи как взятие n
элементов данного потока с последующей декомпозицией каждой пары в свой наибольший элемент. Код для получения такого потока занимает одну строчку.

val s  = 1 #:: Stream.iterate((1, 1)) { case (n1, n2) => (n2, n1 + n2) }.map(_._2)

...

s.take(n).foreach(println)

Вариант №5 - потоковый (Haskell)

Можно ли ещё короче? Haskell позволяет уложиться в 27 символов, но этот код менее понятен, чем предыдущий вариант, и требует понимания работы как библиотечной функции scanl, так и самого Haskell.

fibs = 1 : scanl (+) 1 fibs


>>Click here to continue<<

Scala bin




Share with your best friend
VIEW MORE

United States America Popular Telegram Group (US)