Pull to refresh
10
0
Send message

вывод: функциональные языки ущербны by design.

Вывод не верен в принципе, независимо от истинности/ложности предпосылок. Каждый язык имеет ограничения, но из этого не следует, что этот язык ущербен. Ограничение может быть достоинством языка.

пользователь процедурного языка, как правило, имеет возможность писать функциональный подход

  • отсутствие АТД.

  • отсутствие нужных (для ФП) функций и структур данных в системных библиотеках.

  • громоздкий синтаксис для функциональных конструкций, снижающий выразительность (читабельность).

Эти и другие причины зачастую не позволяют использовать функциональный стиль. как следует.

пользователь функционального языка, как правило, жёстко ограничен функциональными рамками

Наоборот. Пользователь функционального языка, как правило, не ограничен функциональными рамками. Единственный язык с жёстким ограничением, про который я слышал, это Haskell (но это не является недостатком Haskell).

споткнется лиспер, пытаясь разобраться в том, занахрена тут доллар и что значит snd

Я вот знаю, JS, C#, C++. И при этом не могу понять, что в Вашем коде значит "my ($n) = @_;"

Любой, кто изучает Haskell, довольно быстро выучит стандартные операторы. Тем более, что их меньше, чем ключевых слов в C#/C++.

И, кстати, оператор $ всегда можно заменить на скобки. Но с $ читабельность повышается. А вот с C#/C++ нет альтернативы вложенным скобкам (разве что вводить переменные для части выражения).

сперва именно ФП подход заставил вас сделать кривое решение со сложностью 2 в степени N

Не обманывайте. Напоминаю, моей целью было не написать вычисление чисел Фибоначчи, а предоставить Вам пример древовидного рекурсивного процесса.

Всё остальное тоже неверно, на что я Вам уже указывал. Поэтому комментировать не буду.

я не использовал никаких библиотечных функций

Ну так используйте. Чего стоит Ваш перл или любой другой язык без системных библиотек?

доллар и snd остались

Это операторы. Вы тоже используете операторы (например, плюс).

то ваша строка превратилась в четыре

Без системных функций в две, а не в четыре.

Если Вы забыли, то напомню:

iterate f x =  x : iterate f (f x)
fib n = snd $ iterate (\(a,b) -> (a+b,a)) (1,0) !! n

ну вот, у вас получились те же четыре строки что на императивном языке

У меня получилась одна строка.

Если Вы забыли, напомню:

fib n = snd $ iterate (\(a,b) -> (a+b,a)) (1,0) !! n

ещё одно, в общем, подтверждение никчёмности ФП языков

Из неверных утверждений Вы получили неверный вывод. Ничего удивительного.

Ахиллес не догонит черепаху за заданное ((в виде частичной суммы) в условии время. Это время меньше, чем требуется Ахиллесу, чтобы догнать черепаху. В чём парадокс?

И я не верю, что древние греки были настолько тупы, чтобы не понимать этого. Тот же ряд 1/2 + 1/4 + 1/8 + 1/16 + ... слишком очевидно даёт конечную сумму, сколько его не считай.

Понимали. Потому что у этого ряда есть чёткая геометрическая интерпретация: делим прямоугольник ("единицу") пополам, половину - ещё пополам, ... и так далее. В сумме получим площадь не больше исходного прямоугольника (на любом этапе "деления")..

итерате, это, извините проникновение процедурного стиля в язык сектантов.

iterate - это обычная функция с простым кодом

iterate f x =  x : iterate f (f x)

Это не проникновение процедурного стиля в функциональный, а классический пример выделения абстракции. Выделение абстракций для алгоритмов - характерная черта для функциональных языков.

Конечно, код можно было записать и без использования этой функции:

fib n = snd $ f (1,0) n
  where
  f x 0 = x
  f (a,b) n = f (a+b, a) (n-1)

Но писать свою функцию, когда есть библиотечная, плохо.

простая хвостовая рекурсия - тоже конвейер:

Простая хвостовая рекурсия задаёт линейный итеративный процесс. Но и это не конвейер, так как число итераций заранее неизвестно.

Пример конвейера:

f list = take 5 . sort . filter (>0) $ list

или (то же самое)

f = take 5 . sort . filter (>0)

ой ну это буквоедство

В Вашем представлении любая программа на Си является конвейером.

если более выразительный код имеет смысл только как демонстрация

Конечно, нет. Более выразительный код имеет смысл, потому что его проще поддерживать и дорабатывать. Так уж получается, что программисты больше читают код, чем пишут.

и просто выполнить действие N раз у вас не получилось, а получилость 2 в степени N.

У меня получилось именно то, чего я хотел - предоставить Вам пример древовидного рекурсивного процесса.

А вычисление чисел Фибоначчи за O(N) сложности не представляет:

fib n = snd $ iterate (\(a,b) -> (a+b,a)) (1,0) !! n

вот я лучше так запишу

сравните выразительность императивного и функционального стилей.

Императивный: Устанавливаем Счётчик в 0. Выполняем Действие и увеличиваем Счётчик на 1, пока Счётчик меньше N.

Функциональный: Выполняем Действие N раз.

когда Вы пишете fib(n-1) внутри fib у вас именно конвейер fib(fib( получается

Нет.

Конвейер: f(g(h(x)))

Не конвейер f(g(x) + h(y))

только мемоизация куда менее выразительна, нежели обычный цикл с парой переменных,

Наоборот.

В динамическом программировании часто бывает так. Сходу можно записать очевидный (более понятный) алгоритм, использующий древовидный рекурсивный процесс. А потом, после некоторых раздумий его удаётся переписать на менее выразительный, но более эффективный линейный итеративный процесс.

Более выразительный код - значит проще понять, что этот код делает (какую задачу решает).

вот я лучше так запишу

В функциональном стиле тоже можно использовать такой алгоритм и получить сложность O(N). А можно использовать более эффективный алгоритм и получить сложность O(logN).

эта сложность - плата за функциональный подход.

Нет. Эта сложность - плата за выбранный алгоритм.

ещё раз: программирование ряда Фибоначчи - эталонное "не нужно".

Я Вам привёл простейший код с древовидной рекурсией. Мог привести, например, обход в глубину.

fib(fib(fib(fib(fib(fib(fib(fib(

По Вашей схеме функция fib принимает результат (одного) вызова функции fib, то есть, некое (одно) число Фибоначчи. Это неверно для приведённого мной кода. Но такое будет возможно, если (переписать fib) заменить рекурсивный вычислительный процесс на итеративный.

Сможете переписать программу, чтоб дважды не вычислялись эти числа?

Да. Существует несколько способов это сделать. Например, добавить мемоизацию.

никакая реализация SQL не сделает, например, JOIN над TREE-выборками дешевле сложности O(N * log N)

При наличии индексов это неверно, но не суть. Суть в том, что код на SQL будет выразительней (понятней читателю), чем код на Си, который делает то же самое. Поэтому "функциональный" (на самом деле - декларативный) SQL для подобных задач лучше, чем императивный Си.

если человек, нанимающийся на работу, не знает и не хочет знать о таких нюансах, то он не доработает и до конца испытательного срока.

То, что происходит "под капотом", зависит не от языка, а от конкретной реализации языка и от архитектуры конкретной системы. На выразительность языка это никак не влияет.

недаром рядом с SQL каждая уважающая себя БД даёт возможность использовать PL/SQL. Почему? А потому что часто процедурный стиль куда более выразительный.

Не поэтому.T-SQL используется для решения задач, для которых SQL не предназначен. Причём, из-за невыразительности T-SQL эту часть предпочитают вынести в код на основном языке (в моём случае C#), если это не противоречит требованиям (к оформлению и производительности).

часто процедурный стиль куда более выразительный.

Даже простой SQL запрос "select name from table where id = 123" выглядит выразительней (понятней), чем цикл, который делает то же самое. А если добавить join, то разница будет ещё больше.

по использованию памяти разница чудовищная

По использованию памяти разницы нет, так как при хвостовом вызове место в стеке, выделенное для вызывающей функции, уже не нужно. Всё, что требуется, это использовать адрес возврата.вызывающей функции в качестве адреса возврата вызываемой функции.

мой шаг обладает общностью ровно столько же, сколько Ваш

запишите в виде пайплайна

lef fib n =
    if n < 2 then n
    else fib (n - 1) + fib (n - 2)

Information

Rating
Does not participate
Registered
Activity