Pull to refresh

Comments 19

UFO just landed and posted this here

Согласен, нет таких проблем, для решения которых было бы необходимо функциональное программирование. Просто некоторые задачи при функциональном подходе решаются более элегантно, что ли. Например, данные при анализе часто приходится прогонять через несколько функций, и генераторы списков не выглядят красивым решением. Да и if/else и словари — не такие гибкие и удобные инструменты, как паттерн-матчинг.

Мой код для обработки данных зачастую выглядит как-то так:


x_train = np.vstack(list(map(lambda x: x.reshape(1, w, h, 3), map(preprocess, map(np.load, map(lambda e: e.filename, protocol))))))

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

На самом деле, писать про использование математических пакетов в Coconut нечего, потому что используются они точно так же, как в питоне. То есть, можно написать вот так:


from operator import attrgetter
import numpy as np

x_train = protocol |> map$(np.reshape$(?, 1, w, h, 3) ..  preprocess .. np.load ..  attrgetter('filename')) |> list |> np.vstack

и получится рабочий код, который вы можете запустить из интерпретатора или компилировать в питоновский код, эквивалентный вашему примеру (если я правильно его распарсил, конечно). Единственная проблема, которую я тут заметил — нельзя пайпы переносить по строкам без \, но это мелочи и поправимо.

Замечательно, спасбо большое! Вот только attrgetter('filename') не радует, я бы оставил лямбду)

UFO just landed and posted this here
Да-да, совсем как в OCaml, только без строгой типизации (язык-то у нас динамический)

Если быть до конца честным, то Python 3 — строготипизированный, хоть и динамический)

Точнее, строго типизированы операции на встроенных типах. Ничто не мешает вам, к примеру, сделать свой числовой тип, у которого __add__(self, string) преобразует в строку и делает конкатенацию. Конечно, кроме того, что числовых литералов, дающих этот ваш тип, вы не увидите.

UFO just landed and posted this here
Немного смутил размер скомпилированного файла.
-rw------- 1 u0_a209 u0_a209 25 Feb 16 20:07 coco.coco
-rw------- 1 u0_a209 u0_a209 24K Feb 16 20:08 coco.py

25 байт привет мир превратилис в 25К.
Я, конечно, понимаю. Но все-же :)

Ну да, Coconut компилируется в предположении, что на машине, где его будут запускать, есть только чистый Python. Поэтому в выходном файле оказывается довольно много функций и классов, которые лежат мёртвым кодом. С другой стороны, рантайм питона 3.6 весит 100+ МБ, так что лишних 25К на проект вряд ли кого-то будет волновать)

Понимаю, что статья немного не об этом, но ленивые вычисления — тоже часть ФП :)
В примере
sum([n**2 for n in lst])
 

создаётся целый новый ненужный список с квадратами элементов исходного. Это может быть проблемой, если список большой. Избежать этого можно, написав просто
sum(n**2 for n in lst)
 

Такой вариант допускает, к тому же, ленивые вычисления, например
all(f() for f in fun_lst)    # Выполняет функции до тех пор, пока очередная не вернёт False
all([f() for f in fun_lst])  # Выполнит все функции из списка

Ну и в общем случае, если нужно поработать с элементами коллекции, а не с коллекцией в целом, часто бывает удобно привязать к ней итератор
it = iter(lst)
...
next(it)
 

А насколько получаемый код медленней «нативных» питоновских циклов и list comprehension?

Замерил время на возведение в квадрат 100К целых чисел. В среднем, получились такие результаты: Coconut — 87 мс, циклы — 79 мс, генератор списка — 78 мс. Так что язык, скорее всего, немного медленнее питона.

Только возведение в квадрат? Т.е. цепочка преобразований (map -> filter -> map -> reduce к примеру) будет давать еще больше оверхеда? Немного грустно, язык выглядит симпатично.

Прогнал тест с питоновским map'ом — получил те же 87 мс, так что проблема не в Coconut)

Ага, т.е. оно компилится в map. Можно написать оптимизирующий компиль который будет компилить Coconut в списочные выражения =)
Ну и 87 мс для теста это очень мало
То, что «доктор прописал».
Как раз понадобилось немного посчитать и рылся в справочниках.
Знакомьтесь, мои лучшие друзья: Lisp и Clojure. В таких и не только таких случаях, не имеющие проблем с применением на практике.

(reduce + (map (fn [x] (* x x)) (range 1 6)))

Возможно есть более элегентные способы, которые я не учел, это просто сразу в голове было.
Sign up to leave a comment.

Articles