LINUX.ORG.RU

Как стать гуру ФП

 , ,


0

1

Берёшь модно выглядящий ЯП, например coffeescript, пишешь:

size = (xs) -> xs.length
concat = (xs) -> (ys) -> xs.concat(ys)

map = (f) -> (xs) ->
    [y, ys...] = xs
    if (size xs)
        (concat [f y]) ((map f) ys)
    else
        []

Пробуешь:

coffee> (map (x) -> x * 2) [1..5]
[ 2, 4, 6, 8, 10 ]

Потом пишешь filter:

filter = (f) -> (xs) ->
    [y, ys...] = xs
    if not (size xs)
        []
    else if (f y)
        (concat [y]) ((filter f) ys)
    else
        (filter f) ys

Пробуешь:

coffee> filter((x) -> x > 5) [1..10]
[ 6, 7, 8, 9, 10 ]

Теперь ты знаешь ФП лучше среднего хаскелиста и лиспера лора!

А ещё благодаря такому подходу легко можно реализовать модные сейчас трансдьюсеры:

transduce = (fns) -> (xs) ->
    [f, fs...] = fns
    if (size fns) 
        (transduce fs)(f xs)
    else
        xs

Использование:

coffee> transduce([(map (x) -> x * 2), (filter (x) -> x < 10), (map (x) -> x ** 2)]) [1..10]
[ 4, 16, 36, 64 ]

Свёртки:

reduce = (f) -> (xs) -> (m) ->
    [y, ys...] = xs
    if (size xs)
        ((reduce f) ys)((f y) m)
    else
        m

coffee> reduce((x) -> (y) -> x + y)([1..10])(0)
55

reverse = (xs) ->
    aux = (m) -> (ys) ->
        [z, zs...] = ys    
        if (size ys)
            (aux (concat([z]) m)) zs
        else
            m

    (aux []) xs

coffee> reverse [1..5]
[ 5, 4, 3, 2, 1 ]

reducer = (r) -> (m) -> ((f) -> (xs) -> r(f)(xs)(m))
reduceLeft = (f) -> (xs) -> (m) -> transduce([reverse, reducer(reduce)(m)(f)])(xs)
coffee> reduceLeft((x) -> (m) -> x + m)([1..10])(0)
55

reducer нужен для того чтобы reduce(Left) имел интерфейс трансдьюсера (как у map и filter вызванные с функцией, и собственно transduce).

update: Самое главное не забывать называть переменные f, xs, x и тд.

update 2: И да, легко получить RangeError: Maximum call stack size exceededю

update 3: Превратил однострочники в многострочникию



Последнее исправление: holuiitipun (всего исправлений: 8)
Ответ на: комментарий от vimquest

Давай я тебе покажу. Только настоящую лень, а не быдляцкую, как в хаскеле

Это не лень, а pass-by-name для любителей eval.

holuiitipun
() автор топика
Ответ на: комментарий от vimquest

иди читай что такое анафора. Пример Анафоры: «Маша устала, она хочет спать». Пока не напишешь на своем говне это, не возвращайся сюда, ты достал уже своей бестолковщиной.

Ты напиши сперва пример, где это существенно.

Waterlaz ★★★★★
()
Ответ на: комментарий от vimquest

Погоди, но ведь твой предыдущий пример - это тривиальный однострочник на Хаскеле. Без строк, евала и макросов.

Я всего лишь попросил показать что-то более полноценное и выразительное.

anonymous
()
Ответ на: комментарий от anonymous

на Хаскеле

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

anonymous
()
Ответ на: комментарий от vimquest

Давай я тебе покажу. Только настоящую лень, а не быдляцкую, как в хаскеле

VM1772:1 Uncaught TypeError: Illegal invocationmessage: "Illegal invocation"stack: (...)get stack: function () { [native code] }set stack: function () { [native code] }__proto__: Error
holuiitipun
() автор топика
Ответ на: комментарий от vimquest

если бы ты писал читабельный код, я бы ещё попытался его осилить, а так это какое-то ехал function через function

anonymous
()
Ответ на: комментарий от holuiitipun

хз, что там у тебя. Ты print хоть догадался определить? ф-ции print как бы, нет в стандарте. Пиши print=console.log.bind(console), может в этом дело. Ты где проверяешь?

vimquest
()
Ответ на: комментарий от vimquest

Пиши print=console.log.bind(console), может в этом дело. Ты где проверяешь?

Проверял в хроме с:

var print = function(v) {console.log(v);};

holuiitipun
() автор топика

Coffeescript - ваще рульный язык, на самом деле :-) Его бы сделать компилируемым...хотя бы в jvm bytecode.

menangen ★★★★★
()
Ответ на: комментарий от holuiitipun

Знаю, но это не jvm byte code :-) Хотелось бы такой же coffescript со статической типизацией по типу Scala, но не такой вычурный.

menangen ★★★★★
()
Ответ на: комментарий от Reset

Приведи пример «гибкости», что ты под этим имешь в виду?

пайп |> (в F# и ocaml) и , вроде, $ в haskell.

В слабых языках есть гибкость, а в мощном нет??? LOL

vimquest
()
Ответ на: комментарий от vimquest

В моем случае все аргументы равноправны. В haskell можно делать каррирование по первому аргументу, а у тебя в качестве первого аргумента (до точки) всегда должен стоять объект.

Reset ★★★★★
()
Ответ на: комментарий от Reset

В haskell можно делать каррирование по первому аргументу

Оно не нужно внормальном ЯП. И пишется на коленке за секунду.

в качестве первого аргумента (до точки) всегда должен стоять объект.

В качестве первого аргумента может стоять что угодно, лишь бы оно возвращало объект.

vimquest
()
Ответ на: комментарий от vimquest

В слабых языках есть гибкость, а в мощном нет??? LOL

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

fun |> x f = f x
Двуместные функции можно вызывать в инфиксной форме, это уже свойство языка, поэтому можно сделать такой вызов.
x |> f

Отличие от твоей точки только в гибкости.

Reset ★★★★★
()
Ответ на: комментарий от vimquest

Оно не нужно внормальном ЯП. И пишется на коленке за секунду.

Напиши.

В качестве первого аргумента может стоять что угодно, лишь бы оно возвращало объект.

У которого должен быть метод. Выглядит очень криво и несимметрично.

Reset ★★★★★
()
Ответ на: комментарий от Reset

Оппа, вот мы и раскрыли вопрос сисек гибкости. То о чем ты говоришь, это вообще не пайп, а жалкая пародия. Ты пржеде чем применить его, должен сначала определить его. Это ПАЙП??? Данунах. Вот бы тебе в шелле такой пайп, яб поржал. Хотя я и так ржунимогу.

vimquest
()
Ответ на: комментарий от Reset

должен быть метод

Мы о массивах говорим. У всех массивов есть по дефолту все методы работы с массивами. Ты не гони туфту, это бредятина какая то. А то ведь, получается, чтобы вызвать ф-цию с аргументом, надо вначале, подать этот аргумент туда, вот облом то.

vimquest
()
Ответ на: комментарий от vimquest

То о чем ты говоришь, это вообще не пайп, а жалкая пародия.

С чего бы это? Это как раз самый настоящий пайп.

Ты пржеде чем применить его, должен сначала определить его.

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

Reset ★★★★★
()
Ответ на: комментарий от vimquest

Мы о массивах говорим.

А я говорю о произвольных объектах и функциях. У меня в цепочке массив может превратиться во что угодно. И где в итоге гибче? Очевидно, что не у тебя.

Reset ★★★★★
()
Ответ на: комментарий от Reset

А я говорю о произвольных объектах и функциях

нет там никаких проблем с этим


Object.prototype.callme=function(f){return f.call(this.valueOf())}

console.log(
"foo".callme(function(){return this+this})
)

//  foofoo

можно строить произвольные цепочки произвольной длины с произвольными типами. Короче, гибкость 100%, как и положено прототипному яп. Чего не скажешь о том говне которое ты приводишь, где без явной передачи аргументов не обойдешься. Статика и слабость.

vimquest
()
Ответ на: комментарий от vimquest

нет там никаких проблем с этим

То есть прежде чем писать то что ты называешь «пайпом» надо написать кучу таких портянок? Дооо, это «гибкость» ....

Чего не скажешь о том говне которое ты приводишь, где без явной передачи аргументов не обойдешься.

ЩИТО?

Статика и слабость.

Где слабость?

Reset ★★★★★
()
Ответ на: комментарий от Reset

Это говно, а не пайп В нормальных языках есть пайп |> (в F# и ocaml) и , вроде, $ в haskell.

Это круче пайпа, оно реюзабельно и монадка. Ну и оно немного для другого =)

holuiitipun
() автор топика
Ответ на: комментарий от Reset

Пайп в ml-based языках не является языковой конструкцией, а является обычной функцией, которая определяется примерно так

Надо будет как-нибудь поделать что-нибудь на ocaml.

holuiitipun
() автор топика
Ответ на: комментарий от Reset

надо написать кучу таких портянок

Какую кучу? Проснись, эта строка делает все, больше ничего не надо писать.


Object.prototype.callme=function(f){return f.call(this.valueOf())}
Object.prototype.show=function(){var o=this.valueOf(); console.log(o); return o}

"foo"
.callme(function(){return this+this})
.show()
.replace(/foo/, "bar")
.show()
.split("")
.show()
.concat([1,2,3])
.show()
.slice(-3)
.show()
.reduce(function(x, y){return x+y})
.show()

// ::: foofoo
// ::: barfoo
// ::: [ 'b', 'a', 'r', 'f', 'o', 'o' ]
// ::: [ 'b', 'a', 'r', 'f', 'o', 'o', 1, 2, 3 ]
// ::: [ 1, 2, 3 ]
// ::: 6

Где слабость?

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

vimquest
()
Ответ на: комментарий от nikolnik

Зачем извращаться и заново изобретать Ruby?

В каком месте здесь ruby?

holuiitipun
() автор топика
Ответ на: комментарий от vimquest

Какую кучу? Проснись, эта строка делает все, больше ничего не надо писать.

А если тебе в одном модуле нужен show с одним поведением, а в другом - с другим?

holuiitipun
() автор топика
Ответ на: комментарий от vimquest

Я тебе уже написал что является более гибким аналогом твоей точки. Соответственно замени свою точку на $ или |> и будет аналог.

Reset ★★★★★
()
Ответ на: комментарий от Reset

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

vimquest
()
Ответ на: комментарий от Reset

уже написал что является более гибким аналогом твоей точки

Нет, ты написал какое то говно, и, следом, голословное утверждение, что это якобы гибче, однако, в чем заключается гибкость, я так и не увидел. Повторюсь, что ты и пайпа как такогого не показал. То говно, которое надо предварительно декларировать — это не пайп, а просто говно.

Я комбинирую любые объекты в произвольном порядке, без каких=либо костылей.

vimquest
()
Ответ на: комментарий от vimquest

Тебе следует подучить матчасть прежде чем так громко пердеть в лужу.

Reset ★★★★★
()
Ответ на: комментарий от vimquest

Без проблем. Каждый модуль унаследует свою версию.

Попробую объяснить понятней.

Есть библиотека zzlib, в которой определено:

Object.prototype.show = function(){
    var self = this.valueOf();
    document.write(self.text);
    return self;
}

И твой код с:

Object.prototype.show = function(){
    var self = this.valueOf();
    console.log(self);
    return self;
}

И всё, ты сломал библиотеку.

holuiitipun
() автор топика
Ответ на: комментарий от vimquest

что это якобы гибче, однако, в чем заключается гибкость, я так и не увидел.

В том, что все аргументы равноправны и я могу их связывать со значениями как хочу.

То говно, которое надо предварительно декларировать — это не пайп, а просто говно.

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

Я комбинирую любые объекты в произвольном порядке, без каких=либо костылей.

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

Reset ★★★★★
()
Последнее исправление: Reset (всего исправлений: 1)
Ответ на: комментарий от holuiitipun

Ты вообще не понимаешь, как работает Ъ OOP. У тебя каждый модуль — это объект, который наследует свои методы. Имена не пересекаются. Я могу например, в верхнем объекте определить метод call, но он не затрет метод call функции, все будет работать нормально.


Object.prototype.call=function(f){return f.call(this.valueOf())}
Object.prototype.show=function(){var o=this.valueOf(); console.log(o); return o}


"foo".call(function(){return this.split("")}).show()

;(function(x){return x + x}).call(null, 1).show()
//  [ 'f', 'o', 'o' ]
//  2
Все объекты обзавелись новым методом call, но метод call функций никуда не делся.

На уровне модулей, однако, расширять главный прототип не стоит.

vimquest
()
Ответ на: комментарий от Reset

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

vimquest
()
Ответ на: комментарий от vimquest

Все объекты обзавелись новым методом call, но метод call функций никуда не делся.

Ну да, а если ты в обоих модулях вешаешь метод на Object.prototype?

holuiitipun
() автор топика
Ответ на: комментарий от vimquest

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

Я хоть ml не знаю, но могу показать на clojure:

(->> (string/split " " "word by word")
     (map #(str % "!"))
     (filter (compliment "by!"))
     (string/join "/"))

>> "word!/word!"

holuiitipun
() автор топика
Ответ на: комментарий от vimquest

Так нельзя делать, зачем? Если это будет сделано, будет работать последний загруженый, естественно.

Но ты именно так и предлагаешь делать. А с пайпами такой проблемы нет.

Ну и всё таки стоит понимать, что pipe и method chaining(fluent interface) это немного разное.

holuiitipun
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.