Ads
Comments 143
+1
Не извращенство. После функциональных языков такого в джаве ой как не хватает. А на работе писать можно только на джаве :)
+4
Да, пишешь на С++, хочется на Java. Пишешь на Java, хочется на Scala.
0
На С++ описанные в посте конструкции уже лет 10 во всю используются, + имитация лямбд + каррирование (binding параметров). Так что скорее пишешь на Java, хочется на С++.
+4
Вместо join, наверное «более функциональным» было бы реализовать аналог reduce, а уже join можно сделать на его основе.
+1
Согласен с вами.

Просто я хотел попроще, чтобы людям не знакомым с функциональным программированием было понятнее. Ну, и место в статье ограничено.
+2
А чтобы сделать его еще более функциональным, надо было вместо аналога reduce надо было реализовать аналог foldl, на котором реализовать reduce, на котором реализовать join.
0
Списочные выражения к функциональному программированию относятся постольку-поскольку.
-1
Для питонистов это тот уровень функциональщины, который они могут принять:)
0
Ну я лично пишу в том числе и на питоне, однако назвать его функциональным язык не поворачивается)
0
Ну все-таки списочные выражения пришли из ML-языков, насколько я понимаю. Да и выглядят они в точности как описания множеств из учебника по алгебре.

Википедия гласит, что в Python есть следующие элементы ФП:
функции высших порядков, развитые средства обработки списков, рекурсия, возможность организации ленивых вычислений
Это немало:)
+2
Списочные выражения были задолго до ML. Взять хотя бы Лисп. Вообще списки — базовая структура данных и в том или ином виде есть в любом высокоуровневом языке программирования.
И выкиньте свой учебник по алгебре, списки никак не тянут на определение множества хотя бы потому, что в них важен порядок элементов, а в множестве нет. Если точно, список — множество пар вида (i, e), где i — индекс (элемент множества с установленным отношением полного порядка), e — элемент множества-носителя,

list ≡ {(i, e)} | i ∈ I, e ∈ E, (a, e1) = (b, e2) & a = b => e1 = e2.
0
У меня создалось ощущение, что вы не знаете, что такое списочное выражение и чем они отличаются от списков.
+1
Я бы с удовольствием, но плохо объясняю. Однако вот отрывок из учебника на Хаскелю. Автор хорошо показывает, что такое списочные выражения и откуда у них ноги растут. Обратите внимание, синтаксис очень похож на питоновский.
+4
Спасибо, но я знаком с Хаскелем (языком, не с Карри). Списочные выражения, или, точнее, S-выражения — это способ записи, иногда — способ хранения данных. И списки как структура данных обычно реализуются так, чтобы можно было осуществить разделение «голова-хвост»
+3
Ваше неверное представление, видимо, возникло из-за построителей списков в Хаскеле. Так вот, списки иногда (подчеркиваю, только иногда) являются удачным способом для представления множеств. В частности, механизм построения списка основан на теореме, что множество может быть определено с помощью универсума и предиката принадлежности элемента универсума к множеству. Но любая модифицирующая операция над множествами, представленными списками, требует проверки результата на наличие повторов
0
И, кстати, в списках-то индекс как раз и не нужен. Нам нужна только его «голова» (ну и хвост). Это позволяет осуществлять ленивые вычисления.
+1
А кто говорит об индексе как о физически хранимом параметре? Тут важно, что в списке важен ПОРЯДОК элементов. У вас легко один и тот же элемент может быть головой списка и при этом содержаться в его хвосте. В множестве не может быть двух одинаковых элементов по определению
0
> Википедия гласит, что в Python есть следующие элементы ФП:…
Это мало. Эти концепции можно реализовать даже на обычном Си. Однако это не сделает Си функциональным языком программирования. «Функциональность» питона проистекает из-за наличия в нем лямбда-функций, все остальное — просто следствие
+1
Лямбды — это тупо сахар. Ничего в них мистического нет. Куда можно передать функцию, туда можно передать и лямбду, и наоборот.
0
лямбды — это инструмент построения функций. Без них язык не может быть функциональным в принципе. И это не просто сахар, за ними стоит очень мощная теория
+4
А вы не путаете лямбда-исчисление и понятие lambda в терминах питона? lambda в питоне — это просто анонимная функция. Что с ее помощью можно сделать такого, что нельзя с помощью именованной, покажите мне?

Очень мощная теория стоит за тем, что функции являются значениями первого порядка и их можно передавать. AFAIK, это-то и имеет отношение к трудам Алонзо Чёрча.
0
Я не совсем про лямбда-исчисление Черча. Люмбда-выражения применительно к программированию — это способ построения функций «на лету». И за этим тоже стоит теория, только из области компиляции и трансляции. В питоне это действительно просто анонимная функция. Но эта функция может быть создана в процессе исполнения программы
+1
Я могу сходу назвать еще два способа построения функций в рантайме в питоне, никак не вовлекающих лямбды. И что? Давайте теперь молиться на декораторы и метод __call__?
0
__call__ и декораторы — это совсем из другой степи, не имеющей отношение к функциональному программированию, и кстати не позволяют породить новые функции
0
а лямбды-то как порождают новые функции, просветите?
0
как конкретно интерпретатор Питона это делает — не знаю. Хотя ради Вас пороюсь в исходных кодах. А вот само «порождение» функции — пожалуйста:

def multN(value) : return (lambda x : x**value)
0
class MultN(object):
    def __init__ (self, power):
        self.power = power

    def __call__ (value):
        return value ** self.power
0
Добавляем в класс соответствующие методы, используем apply.

Вы что, хотите доказать, что функтор не идентичен анонимной функции?
0
Я хочу показать, что есть принципиальная разница между объектом, который ведет себя по-разному в зависимости от внутреннего состояния, и функцией высшего порядка, которая производит функцию, удовлетворяющую некоторому условию
0
Если поле power в моем примере сделать приватным, то с точки зрения пользователя не будет разницы.
Можно импортировать вашу функцию и мой класс из разных пакетов, и без рефлексии и проверок типа вы никогда не узнаете, что есть что.
-1
Дело не в том, что нельзя получить такую же функциональность другими методами. В большинстве случаев можно. Просто само понятие lambda было введено в Питон с целью избавиться от выражений вроде приведенного Вами. И это отнюдь не синтаксический сахар. Это совершенно другой механизм выполнения тех же действий
0
Я согласен, что пользоваться лямбдами в вашем синтетическом примере удобнее, чем моим классом, который куда более громоздок.

Но вы утверждали, что только лямбды позволяют порождать новые функции. Функтор, конечно, не совсем чтобы функция, но и лямбда не идентична выражению def.
0
Я утверждал и утверждаю, что лямбды — это не синтаксический сахар. Есть огромное количество случаев, когда применение безымянных функций «естественно», в отличие от объектов (как, собственно, в синтетическом примере). И если абстрагироваться от особенностей конкретного языка, то такое использование объектов возможно только для языков интерпретируемых, в то время как лямбды вполне хорошо живут и в компилируемых
0
Эмм, тут в топике, под которым мы развили этот холивар, вообще-то написано, как в компилируемом языке делать функторы. И как раз лямбд там нету (пока).

Такое использование объектов свойственно языкам, в которых есть перегрузка оператора оператора «вызов функции».

Нет, такое использование объектов свойственно только тем языкам, в которых функции не являются гражданами первого класса.
0
Нажмите ctrl+home, чтобы вспомнить. Нажмите ctrl+w, чтобы забыть.
0
Может я Вам открою глаза на мир… Но Java не является компилируемым языком в том смысле, что не компилируется в машинный код
0
Теоретически может существовать железо, для которого байт-код джавы будет являться ассемблером. Ну, а пока такого железа под рукой нет, будем его виртуализировать.
-2
Увы, но как-раз теоретически такого железа пока не придумано (мне теория такого железа неизвестна). Именно в перегрузке оператора вызова во время исполнения загвоздка. Можно определить N-е количество объектов с разными версиями этой функции, но нельзя получать их в процессе выполнения, это противоречит архитектуре фон Неймана. Поэтому на данный момент такая функциональность достижима только с использованием лямбда-исчисления. И именно по этой причине нет компилятора Питона. Просто нет таких технологий компиляции
0
JIT (Just In Time) производит исполнение, перевод в машинные инструкции «на лету». Но прямого соответствия между JIT-кодом и машинными командами для заданной архитектуры нет. То есть это все равно процесс интерпретации, хотя и более низкого уровня
0
Хм, ну тогда и компиляция — это «процесс интерпретации, хотя и более низкого уровня». Разница между компиляцией и интерпретацией зачастую только в том, что результат компиляции сохраняется в ПЗУ в виде файла.
0
Это принципиальная разница. Пока никому не удалось реализовать в железе рефлексию и интроспекцию
0
В железе может и нет, а вот в ассемблере — запросто, мне кажется:)
0
Не правда, в Java нет никакого JIT-кода. Есть исходный байт-код, а есть машинный код, в который он компилится на основе статистики.

И можно в байт-код, кстати, вручную скомпилировать вручную что угодно. Хотя функтор. И он скомпилится JIT-от.
0
JIT-кода в природе нет, тут я маху дал. Это технология. И ради Бога, компилируйте в байт-код что угодно (условно), но это все равно байт код. Я говорю о том, что нет прямого соответствия между байт-кодом Java и машинным кодом процессора.
0
А что значит нету соответствия?

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

И вообще, ну вот напишу я на java примерно следующий код:

return Functions.componse(fn1, fn2);

Этот код создаст новый функтор и вернет. Можно с помощью ветвления задавать различные способы формирования объекта.

И эта функция будет транслирована в машинный код.
0
Эта функция будет ТРАНСЛИРОВАТЬСЯ. Нет соответствия означает, что нет (опять же, мне неизвестно) способа транслировать программу на Java в набор машинных инструкций минуя байт-код. Также как такого способа нет для Питона, Руби и многих других современных языков. Процесс выполнения Java-программы — это процесс интерпретации байт-кода Java-машиной
0
>способа транслировать программу на Java в набор машинных инструкций минуя байт-ко

А что меняет наличие этого промежуточного звена?

>Процесс выполнения Java-программы — это процесс интерпретации байт-кода Java-машиной

Это не так. Процесс выполнения java-программы, это выполнение машинного кода. Интерпретация совершается на ранних стадиях работы программы пока JIT не собрал достаточно статистики для компиляции.
0
Тогда где экзешники Java-программ? Байт-код, как Вы наверное знаете, очень хорошо документирован. Существует GNU-версия компилятора Java, который компилирует в машинный код, однако полной совместимости с языком добиться так и не удалось. Потому что не все так просто. Java-машина помимо сбора статистики (которая, кстати, может помочь оптимизировать процесс интерпретации, но никак не исключить его) делает еще кучу вещей, реализация которых в машинных инструкциях привела бы к необходимости добавлять исполняющую среду в каждую скомпилированную программу. Вот по этому и производится компиляция в байт-код. Потому что дальше этого сделать что-либо весьма проблемно
0
Exe-шники в памяти.

И, конечно, AOT-компиляторы добавляют рантайм к каждому exe-нику.

>Вот по этому и производится компиляция в байт-код.

Не по этому. Вполне можно рантайм как динамическую библиотеку таскать за собой. Байт-код же для кроссплатформенности и более эффективной компиляции за счет сбора статистики.
0
Интересно. Получаем следующую картину. Байт код эквивалентен машинному в том смысле, что может быть в него преобразован. Для большинства языков есть компиляторы в байт-код Java. То есть все языки — компилируемы! Ура товарищи, все проблемы решены!
0
А кто сказал, что компиляция в машинный код решает все проблемы производительности? Скомпилироваться то оно скомпилируется, просто не эффективно. Потому как из не эффективного байт-кода получается неэффективный машинный код.

К примеру, есть компиляторы языка scheme, которые дают настолько не эффективный исполняемый код, что он проигрывает даже некоторым интерпретаторам scheme.
0
Про scheme история отдельная. Там компиляторы были столь ужасны, поскольку все основные механизмы ставились на костыли. И все же пример показательный. Даже для языка с таким простым синтаксисом, как scheme, не удалось создать достойный компилятор. Java — в десятки раз сложнее даже с синтаксической точки зрения. Поэтому я и говорю, что байт-код — это вынужденная мера. Компилятора Java в машинный код нет и не предвидится не потому что это никому не нужно. Желающих навалом. Только не удается это сделать
+1
Извините, но вы какую-то ерунду пишете.

> Даже для языка с таким простым синтаксисом, как scheme, не удалось создать достойный компилятор. Java — в десятки раз сложнее даже с синтаксической точки зрения.

Возможность оптимизации практически никак не коррелирует с синтаксисом языка. Для scheme нету компиляторов потому же что и для питона — отсутствие декларации типов, например как в Common Lisp который, кстати, прекрасно компилируется в машинный код.

>Компилятора Java в машинный код нет и не предвидится не потому что это никому не нужно. Желающих навалом.

Вот это уже откровенный бред. Так, на вскидку: www.excelsior-usa.com/jet.html

Популярностью особо не пользуется, так как желающих не так уж и много. JIT компиляция имеет одни единственный минус — тупеж приложения сразу после старта. Приходится приделывать всякие грелки.
-1
Всего один вопрос. Что делает JVM? При запуске Java-программы один раз создается набор машинных инструкций — эквивалентная программа — и после этого JVM с чувством выполненного долга отдыхает? Потому что если нет, то речь идет об интерпретации байт-кода.

> Для scheme нету компиляторов потому же что и для питона — отсутствие декларации типов

Ох, если бы все было так просто…
0
> При запуске Java-программы один раз создается набор машинных инструкций — эквивалентная программа

Практически да. Там есть несколько оговорок, правда.

>— и после этого JVM с чувством выполненного долга отдыхает?

Не отдыхает. По крайней мере осуществляет сборку мусора.

0
Тогда я неправ. И все-таки рисуемая Вами картина выглядит черезчур идеалистической. Если виртуальная машина (по-сути исполняющий механизм) оказывает какое-либо управляющее воздействие в процессе исполнения программы, то эквивалента в машинных кодах нет. А если такого воздействия нет, то непонятно, почему это называют виртуальной машиной, ведь в этом случае ее функции ограничиваются JIT-компиляцией.
+2
Там фишка в том, что код, который редко вызывается не трогается JIT-ом, а интерпретируется. Ну и интерпретация врубается сразу после старта приложения, пока основная часть кода не сжитится. Управляющего воздействия вроде бы нету, по крайней мере не слышал.
0
Ну это понятно, очевидная оптимизация. Но слабо верится, что функции виртуальной машины ограничиваются вышеперечисленными. Плотнее займусь этим вопросом
0
Хотя в моем представлении куда более правдоподобной выглядит картина, кода часть программы, та часть, где это возможно, компилируется JIT-ом, а остальная, в которую входят рефлексия и описанные в топике вещи, интерпретируются в процессе исполнения. Очень сложно представить соответствующий таким штукам набор машинных команд
0
Есть принципиальная разница между интерпретатором и рантаймом, Вам не кажется? Питон тоже можно таскать собой как библиотеку с байт-кодом, это не делает его компилируемым, потому что все равно осуществляется процесс интерпретации. В отличие от Си, в котором иногда происходит обращение к функциям библиотеки времени исполнения
0
Конечно есть. Я вообще это все писал не в защиту питона, а как доказательство того, что только реализация лямда-исчисления позволяет формировать функции в рантайме.
0
В питоне нет лямбда-исчисления. Только безымянные функции, по-старинке названные лямбдами. И все-таки такая нехитрая абстракция позволяет-таки создавать функции в процессе выполнения. Ограниченно, но позволяет
-1
Соответствия нет. Когда в программе на Java присутствует многопоточность, потоки имитируются Java-машиной, а не средствами операционной системы. Когда происходит рефлексия — идет анализ байт-кода, а не машинного. Байт-код обеспечивает кроссплатформенность — бесспорно. Более эффективную компиляцию? скорее исполнение. Но не только. Это еще и вынужденная мера, позволяющая реализовать все средства языка
0
Посмотрел. Частичная компиляция + альтернативная реализация Java-машины, не более
0
Подождите-ка. В C++ есть перегрузка оператора (). Вы же не скажете, что это не компилируется.
0
Перегрузка — процесс статический. Динамически реализовать его никак нельзя
0
Интересно. Буду знать. Хотя мне сложно представить такой процессор
0
Можно нагуглить подробности. Я не очень в процессорах разбираюсь, поэтому не стал искать, но инфа есть.
0
Увы, но как-раз теоретически такого железа пока не придумано (мне теория такого железа неизвестна)

То, что вам что-то неизвестно, ещё не означает, что этого не существует в природе: en.wikipedia.org/wiki/Java_processor.

Вообще, разработка процессоров специально для байт-кода высокоуровневого языка началась отнюдь не с Java. Ещё задолго до Java-машин существовали Lisp-машины и Smalltalk-машины.
0
было уже «практически»,
цитата «Компания Sun на Микропроцессорном форуме представила процессор MicroJava 701. Это первый процессор, непосредственно исполняющий байт-код Java.»
источник — www.osp.ru/cw/1997/42/25003/
(«Computerworld Россия», № 42, 1997 )
0
А именно это делает язык функциональным. Если основная единица — объект. То язык объектный. И можно говорить только об элементах других парадигм программирования
0
Тут ниже высказывалось мнение, что функциональные языки — это те, в которых все функции чистые. Я вот как-то с этим больше согласен.

Но я никогда и не утверждал, что питон — функциональный язык. Но функциональщины там довольно много, и это не только лямбды и списочные выражения.
-1
Чистота функций — следствие, не причина. Списочные выражения — тоже. Парадигму составляют не какие-либо технологии, а система базовых установок, если угодно — идея. Хаскель — функциональный язык, однако писать на нем можно без использования списочных структур и с использованием изменяемых состояний. А вот лямбда-выражения — почти краеугольный камень
0
Кстати, apply позволяет сделать каррирование, что, в общем-то, порождает новую функцию.
0
Об этой возможности не знаю, напишите пожалуйста поподробней. Хотя сути в общем-то не меняет. Каррирование просто частный случай. Например в Хаскеле оно определяется очень просто через те же лямбды:

carry f (x, y) = \x y -> f (x, y)
0
Тут я погорячился малость, простите — все-таки apply в питоне возвращает не функцию, а результат выполнения.
0
И да, рыться не надо, спасибо:) Мне не интересна в данном случае конкретная реализация.
0
Способность строить функции на лету — это свойство рантайма. Лямбда функции — это просто один из синтаксисов, позволяющих этим свойством воспользоваться, вам тут правильно написали.
0
Тогда всё — способности рантайма. Любая возможность языка — просто свойство рантайма
+1
Не только рантайма. Некоторые вещи обеспечиваются на этапе разбора исходного кода, а некоторые обеспечиваются с помощью рантайма. Никакой магической математики под капотом там нет.
+1
Пожалуйста, поясните, почему вы считаете люмбды «тупо синтаксическим сахором», а списочное выражение нет?

Вам не кажется, что любой язык высокого это синтаксический сахар над набором машинных команд? И в чем тогда по-вашему состоит важное отличие функциональных языков программирования от других?
0
Списочные выражения значительно повышают выразительность кода и привносят новую семантику. Особенно вкупе с генераторами.
Лямбды — нет.

Вам не кажется, что любой человеческий язык — это синтаксический сахар над криками и жестами?
Действительно важное отличие для меня состоит в совершенно отличной семантике. Так же часто упоминают отсутствие побочных эффектов и ленивые вычисления.
0
По-моему, единственной важной особенностью как-раз и является отсутствие побочных эффектов, все остальное можно накалякать сверху самому. По этому когда говорят, что человек пишет в функциональном стиле на каком-то языке, имеют ввиду, что он старается минимизировать изменяемые состояния. И по этому map > for.

Питоновская лямбда в свою очередь имеет действительно другую семантику, конечно, это не честно каррированная функция, но и не просто анонимная — в ней напрямую нельзя изменять состояния (lambda: c = 5 не покатит), хотя можно изменить косвенно, по тому, что окружение не «чистое», вроде (lambda x: x.y()).
0
Поясню. Допустим, мы имеем только чистые функции (без побочных эффектов, чтобы было проще возьмем только от одного аргумента, имена функций и аргументов не существенны, по этому их опустим), для того, чтобы при помощи них произвести какие-то вычисления нужно определить список операций, которые мы можем над ними выполнять. Алонзо Черч показал, что минимально необходимых операций всего 3 — α-преобрзование, β-редукция, η-преобразование.

Имея только чистые функции и 3 операции можно провести любые вычисления (язык полный по Тьюрингу), такой язык называют λ-счислением. Например, построить операцию рекурсии (Y-комбинатор).

Но так как каждый раз записывать Y-комбинатор или числа в Church numerals муторно и бессмысленно, то напридумали разного синтаксического сахара, например функции от нескольких аргументов (define в лисп) и тп
Таких функций напридумывали много (map, fold, reduce и тд) и язык реализцющих их каким-то образом, но не реализующий лямда-счисления, называют языком с функциональными возможностями, которым является Python. Lisp в свое время и был реализацией лямбда-счисления, по этому он просто функциональный язык.
+1
Все верно, с одним замечанием. Y-комбинатор относится к комбинаторной логике, а не к лямбда-исчислению. Хотя они и изоморфны
0
И все-таки — одно дело, когда сверху сам калякаешь, а другое, когда язык это поощряет. Любая реализация тех же списочных выражений в виде некоторой функции будет выглядеть более громоздко.

Видимо, я был неправ, когда говорил, что лямбды в питоне не несут никакой новой семантики. Во-первых, я не знал про «lambda: c = 5 не покатит». Во-вторых, я пишу на JS, а там нет различия меду анонимными функциями и именованными (за тем исключением, что именованные подвергаются «поднятию», но я не могу придумать красивый не синтетический пример, когда это меняет их использование).
0
Или, другими словами — где та грань, где синтаксический сахар переходит в новую семантику? Как вы считаете?
0
эта грань в мозгах, а не в языке. Кто то на Java как фортране пишет, кто то на С в объектном стиле.
+1
Функциональное программирование — это в первую очередь иммутабельность данных, а уж потом все манипуляции с функциями, как со значениями.
0
В функциональном программировании не функция является объектом, а все элементы языка могут быть интерпретированы как функции. Поэтому можно говорить только об элементах функционального программирования в Java, Python и прочив мультипарадигмальных языках
+3
Функциональное программирование — это идея в головах людей(или если хотите, подход к решению), а не конкретный язык или набор фишек, впрочем стоит отметить, что определенный набор-таки позволяет сделать это проще и естественее.

А вообще да, как человек, который пишет на java и функциональных языках, могу сказать, что редко мешаю стандартные вещи из Java с идеями подчерпнутыми из функциональной среды — поэтому было бы невероятно интересно увидеть продолжение.

мое имхо, что стоить иметь хороший набор методик из разных источников и парадигм, чтобы выбрать что-то лучшее для решения задачи, но в реальной жизни иструменты довольно сильно ограничены, поэтому написанное выше отличный компромис между форсированными сверху инструментами и выбором идей для решения задач.
0
Спасибо за хороший отзыв. Буду работать над продолжением. :)

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

Стараюсь придерживаться того же подхода.
-1
Все попытки разбавить императивный поноскод на Java какими-либо костылями приводят в ужас. Приведенный в этом посте map имеет очень печальную, неленивую реализацию как по выделению памяти, так и по выполнению. Дальше читать уже не имело смысла.
0
Со всем уважениемм и благодарностью за проделаную работу.

Как Function<F, T> реализует first class function?
А если не реализует — функциональное ли это программирование?

Скажем в javascript делаем так:

function outer () {
var ctx="внутри";
var inner = function() {
return ctx;
};
return inner;
}

var ctx="снаружи";
var res = outer();
console.debug(res());



Как это реализуется на Java?
-1
first class function означает, что функции могут быть аргументами функций и их возвращаемыми значениями. Function все это обеспечивает.
-1
это делегаты, не замыкания
есть в совсем не функциональных паскале и си, например

замыкание, иже closure или first class function, должно еще уметь таскать за собой контекст, что и делает функциональщину такой приятной и полезной на асинхронных алгоритмах

и Function<F, T> из примера, строго говоря, контекст тащит, хоть и некими ограничениями
но я не пойму почему в статье об этом ни слова
-1
>есть в совсем не функциональных паскале и си, например

А по-вашему в не функциональных языках не может быть элементов функциональщины? Ссылка не процедуру в Си вполне себе элемент функциональщины.

А замыкание, это замыкание. Понятие ортогональное first class function. И если вам интересно, то в Java они с оговоркой есть. Можно внутри анонимного класса обращаться к final ссылкам и полям внешнего объекта.
0
Ссылка на процедуру в Си вовсе не есть элемент функциональщины. По этому вопросу, собственно, и все дебаты. Как было правильно замечено ранее, функциональное программирование не определяется набором возможностей языка, это образ мышления. Если функцию можно рассматривать в математическом смысле, т.е. как отображение одного множества на другое, то это — функциональный язык. Поэтому Си — не функциональный, а императивный язык программирования
0
>Если функцию можно рассматривать в математическом смысле, т.е. как отображение одного множества на другое, то это — функциональный язык.

И что же нам мешает это делать в Си, тем более вы говорите, что ФП-это идея?

> Поэтому Си — не функциональный, а императивный язык программирования

При чем здесь «поэтому»? Си процедурный язык, так как он поощряет такую парадигму. И никак не запрещает другие, например объектно-ориентированную (см. GObject), метаязыковую (привет макросам) или функциональную (опять же привет указателям на процедуры).
-1
Где в Си наследование, инкапсуляция и полиморфизм, чтобы его можно было считать объектным? Макросы там определяют процесс компиляции и только, к алгоритмике не имеют никакого отношения. И когда Вы сможете на Си сделать то же каррирование — тогда можно будет поговорить о функциональности. А так, простите, процедурный язык и только
0
>Где в Си наследование, инкапсуляция и полиморфизм, чтобы его можно было считать объектным?

Читайте документацию по GObject.

>Макросы там определяют процесс компиляции и только, к алгоритмике не имеют никакого отношения.

А при чем здесь алгоритмика? Речь идет о кодогенерации

>И когда Вы сможете на Си сделать то же каррирование — тогда можно будет поговорить о функциональности

Извините, говорить о чем? Я вам говорю, что ссылки на процедуры — элемент функционально программирования. Маленький такой, куцый, но элемент.

>А так, простите, процедурный язык и только

А я разве доказываю обратное?

0
Я так понимаю, у нас с Вами разные исходные убеждения. Для меня неприемлемо говорить о поддержке какой-либо идеи даже элементно, если не отражены основные концепции. То, что в Си можно присваивать переменной адрес функции, не является элементом функционального программирования, потому что сама концепция присваивания функции, именно функции, чужда этому языку. Речь может идти о присваивании адреса, что можно реализовать и на ассемблере. И я не думаю, что найдется человек, всерьез утверждающий, что ассемблер поддерживает функциональную парадигму программирования
+1
Да я, расцениваю все с чисто практической точки зрения. Если можно добиться сходных результатов — это «оно».
0
ta6aku, спасибо за конструктивную критику.

В моей статье действительно ничего не было сказано о замыканиях, хотя это один из ключевых элементов функционального программирования. Уверяю вас, что замыкания в Java можно использовать также как и в JavaScript. Наверное в следующий раз стоит начать статью именно с этого вопроса.
0
Вот тут вижу выше много споров о том функционально/не функционально. Товарищи спорщики, вы бы вначале договорились о том, что такое функциональное программирование, а потом уже спорили. Я когда разбирался с этой терминологией и пришел к выводу, что определение у термина очень размытое. Есть некий набор свойств: функции как первоклассные значения, immutable данные, рекурсия, паттерн матчинг, алгербраические типы данных и так далее. При этом никакой набор признаков нельзя назвать необходимым или достаточным.
-1
eliah_lakhin, Если возможно, расскажите, в чём преимущества «функциональной» реализации по сравнению с «традиционной». Просто из примера, это не очевидно: кода стало больше + синтаксис generic'ов усложняет чтение.
0
Одно из преимуществ, например, заключается в том, что метод main, изначально написанный в императивном стиле(«традиционном»), занимал 6 строк, а после переписывания в функциональном стиле стал занимать всего одну строку.
0
main? Насколько я понял, он состоит только из вызова joinNumbers и для мало что изменилось при переходе к функциональной реализации.

А вот кода стало больше (INT_TO_STRING + map +join > императивный joinNumbers)

И появились конструкции вроде
public static <F, T> List map(Collection from, Function<? super F,? extends T> transformer) {
0
> А вот кода стало больше (INT_TO_STRING + map +join > императивный joinNumbers)

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

А можно вообще ничего не писать, просто взять уже готовый Google Collections или Apache Common Collections.
+1
Странно, что никто не откомментировал про Clojure. Функциональное программирование без всяких извращений.
+1
А что про него комментировать? Котлеты отдельно, мухи отдельно. Являясь большим фанатом clojure, я пишу участки, требовательные к производительности, на Java. И в Андроид пока clj не засунешь (я пробовал — 7 секунд на запуск пустого приложения). Если сравнить jvm-байткод с ассемблером, то Java сейчас — это С. Есть множество более экспрессивных и concise языков (Scala/JRuby/Groovy/Clojure), но что должно работать быстро, все равно пишется на Джаве.
А вот-такие штуки для Джавы вроде Google Collections — это как С++ как С. Код выглядит как дерьмо, но зато можно сделать больше, не теряя производительности.
0
Давно было интересно, как можно писать функционально на Java. Однако эта статья не ответила на основные мои вопросы, которые возникают сразу после объявления интерфейса Function:

1) Как реализуется композиция: λxyz.x(yz)?
2) Типы (которые всё-таки вводятся, т.к. интерфейс-то параметрический), как я понимаю, образуют категорию, допускающую экспоненцирование и умножение. Как реализовать контроль типов? Более точный вопрос: как выглядит функция каррирования, ведь для этого нужно как минимум определить произведение?
С экспоненцированием проблем таких нет, т.к. Function, будучи интерфейсом, может рассматриваться как тип аргумента/результата (что, собственно, и подчеркивается в статье)
0
1) В терминах Google Colletions композиция может быть реализована с помощью Functions.compose. Она принимает две функции и создает инстанс новой функции, которая при вызове осуществляет применение(Function.apply) переданных функций.

2) Каррирование можно реализовать, например, одним из двух способов:

Способ первый. Объявить порядка семи-восьми функций, принимающих разное количество аргументов, и имеющих разный тип(Function0 — от нуля аргументов, Function1 — от одного аргумента и т.п.). Для каждого типа функций объявить метод apply с меньшим числом аргументов, который будет возвращать каррированную функцию.

Способ второй. Объявить гетерогенные коллекции(кортежи, «параметрические бины»), опять таки, для разного количества аргументов. Ну и сделать методы каррирования для функций от таких аргументов.

Отмечу, что в обоих случаях можно сделать наследование между функциями и/или кортежами, то есть, например, кортеж от пяти аргументов — это частный случай коллекции от трех аргументов. Это позволит немного унифицировать работу с функциями разных типов. Также в обоих случаях мы имеем ограничение на число аргументов, правда передавать больше шести-семи аргументов тоже не очень хорошо(в Haskell, насколько я помню, гетерогенные коллекции тоже ограничены по длине).

В общем, оба способа на самом деле не универсальны, имеют свои плюсы и минусы, однако в целом позволяют решить проблему типобезопасным образом.
0
Вы видели Scala?
Там все проще делается, без попыток заставить императвную Java быть функциональной.
0
Тогда непонятно желание протащить это на Java. Разве что для развлечения :).
Кстати очень напомает linq в C#.
+3
Статья ориентирована на Java программистов, которые хотели бы познакомиться с ФП. Мне кажется, что примеры на Java конструкций, имеющих аналог в функциональных языках, облегчит задачу перехода к функциональному программированию.

А потом, иногда бывает, что в силу обстоятельств возможности перейти с Java на что-то другое просто нету, а желание использовать функциональные конструкции есть. Конечно эти конструкции в Java получаются гораздо более громоздкими, чем, скажем, в Scala, но все-таки для решения некоторых задач могут быть полезнее и лаконичнее аналогичных решений в императивном стиле.

Ну, и да, конечно для развлечения тоже. :)
+1
Пользуюсь Google Collections как раз для этих целей. Очень удобно.
0
Пользуюсь иногда Guava, и каждый раз когда идет порыв на использование transform, щелчок в мозгу — кода столько же, засирается PermGen, а результат один. В итоге пишу обычный цикл.

Цееность таких извратов в стандартной яве для меня сомнительна. Как когда то писали не помню кто — если хотите писать на паскале, пишите на паскале, а не делайте макросы begin и end
+1
Иногда(не всегда, конечно) функциональный подход может оказаться лучше в плане дизайна. Например, он позволяет облегчить задачу разделения системы на отдельные компоненты для дальнейшего их переиспользования.

> кода столько же

0
ну с этим я согласен, просто в джаве это не получается так элегантно и красиво — пока что. Вот выйдет восьмерочка с лямбдами — заживем. Там тоже не так красиво как в рубях или лиспе, но код будет короче, а значит и профит тоже хоть какой то будет (с коментом ниже я тоже согласен — очень удобно, пожалуй это один из немногих случаев когда я смело использую гуаву).
0
> кода столько же

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

хотели бы познакомиться с функциональным программированием, но не имеет возможности/желания изучать Haskell/Scala/Lisp/Python, — эта статья специально для вас.


то есть, если у вас дома нету интернета и нет желания учить фп — вот пожалуйста вам в наказание, горите в аду!!!
Only those users with full accounts are able to leave comments.  , please.