Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, <Аноним>, Вы писали:
А>> Ну и примитивные применения: map ((*) 2) [1..10], и типа того.
VD>Ну, и чем это более понятно чем: VD>
VD>[1..10].Map(_ * 2)
VD>
VD>?
О! Теперь понятно что аноним написал
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
V>Теперь бы понять, зачем оно применяется. И ещё маленький вопрос, тут предполагается, что а и b обязательно функции?
Легкий способ использования HOF. Сразу два карринга: increment_them_all = map (1+) — map f (отсутствует список) и (1+) (отсутствует второй операнд сложения). Сравни с increment_the_all_explicitly list = map (\x -> 1+x) list
Вывод типов в системе с каррингом слегка удобнее.
Yours truly, Serguey Zefirov (thesz NA mail TOCHKA ru)
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Vermicious Knid, Вы писали:
А>>>> Ну и примитивные применения: map ((*) 2) [1..10], и типа того. Т>>>Но чтобы поделить на 2 уже придется делать map (flip (/) 2) [1..10] VK>>Можно просто map (/ 2) [1..10], (/ 2) равнозначно (flip (/) 2).
VD>Вы бы пояснили смысл сказанного вами. А то боюсь, что многие не поняли что такое flip и зачем он нужен, но спросить стесняются.
Ща попробую, как тут обычно делают
OCaml:
let rec flip f b a = f a b
Haskell:
flip f b a = f a b
Nemerle:
def flip (f,b,a)
f(a,b)
Scheme:// тут не уверен ;)
(define (flip f b a) (f a b))
// ну и попробую пожалуйErlang:
flip( f, b, a ) -> f( a, b )
Vermicious Knid wrote: > Вот на OCaml пишется крайне много парсеров, при этом карринг там есть, но в этих парсерах я его осознанного и целенаправленного применения(в смысле всяких комбинаторов итп) не видел. Ostap.
Могу еще свой велосипед с треугольными колесами показать, но ты его тем
более зарубишь, на нем парсеры выглядят так:
let digit_p =
item_p >>= function'0' -> return 0
| '1' -> return 1
| '2' -> return 2
| '3' -> return 3
| '4' -> return 4
| '5' -> return 5
| '6' -> return 6
| '7' -> return 7
| '8' -> return 8
| '9' -> return 9
| _ -> fail
let unsigned_p =
one_or_more digit_p >>= fun digits ->
return (List.fold_left (fun x y -> x*10 + y) 0 digits)
let sign_p =
item_p >>= function'+' -> return 1
| '-' -> return (-1)
| _ -> fail
let opt_sign_p = optional sign_p 1
let integer_p =
opt_sign_p >>= fun sign ->
unsigned_p >>= fun x ->
return (sign * x)
let fractional_part_p =
literal_p '.' >>
unsigned_p >>= fun f ->
return f
let real_p =
opt_sign_p >>= fun sign ->
unsigned_p >>= fun i ->
optional fractional_part_p 0 >>= fun f ->
return (make_real sign i f)
Вообще смысл спора непонятен. Человек спросил, как можно применять
карринг. Ответ — кроме очевидных применений вроде map (+1), можно делать
карманные DSL без макросов и прочей тяжелой артиллерии. Понятно, подход
имеет ограничения. Но зачем опять доказывать, что Немерле лучше всех, а
Хаскелл — игрушки для математиков, и причем тут рукописные парсеры — .
lomeo, Вы писали:
T>> Если есть функции высшего порядка, то любую функцию можно определить в каррированной форме.
L>Э-э-э... Тут не понял. Какая связь?
ie wrote: > > Ща попробую, как тут обычно делают > [ccode] > OCaml: > > let rec flip f b a = f a b
rec лишний > > Haskell: > > flip f b a = f a b > > Nemerle: > > def flip (f,b,a) > f(a,b) >
Скорее def flip (f) = fun (x,y) { f(y, x) }
Кстати, пример очень в тему, показывает, чем отличаются языки с
каррингом от языков без . > Scheme: > // тут не уверен > (define (flip f b a) (f a b))
аналогично случаю Nemerle. > // ну и попробую пожалуй > Erlang:
Аналогично случаю Nemerle.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Vermicious Knid, Вы писали:
А>>>> Ну и примитивные применения: map ((*) 2) [1..10], и типа того. Т>>>Но чтобы поделить на 2 уже придется делать map (flip (/) 2) [1..10] VK>>Можно просто map (/ 2) [1..10], (/ 2) равнозначно (flip (/) 2).
VD>Вы бы пояснили смысл сказанного вами. А то боюсь, что многие не поняли что такое flip и зачем он нужен, но спросить стесняются.
Смысл такой, что ((*) 2) не аналогично твоему (_ * 2). Оно аналогично (2 * _) (Хоть для чисел результат и будет одинаковым).
Для того, чтобы поменять местами первые два аргумента используется функция flip, т.е. (flip(f))(x,y) = f(y,x). На самом деле (/2) о flip не знает, но занимается тем же самым, т.е. создаёт лямбду (\x -> x / 2), впрочем (100/) тоже возвратит результат как лямбду (\x -> 100 / x), так что flip тут не при чём, это стандартный сахар Хаскеля для infix записи.
Что касается темы (карринг), то тут уже советовали посмотреть на Parsec. Тогда его преимущества (карринга) сразу станут видны — это краткость и понятность записи.
Здравствуйте, Vermicious Knid, Вы писали:
VK>Nemerle currying и не нужен, равно как и функция flip. Вместо (flip f 2), т.е. flip(f)(2, _) в нотации Nemerle, можно спокойно писать f(_,2).
Это все равно, что сказать — в Хаскель flip не нужен, можно спокойно писать (`f` 2)
VK>Ну и скажем f(1,_,2) вместо чего-то вроде: VK>
VK>middle f a c = \b -> f a b c
VK>middle f 1 2
VK>
Ну какой нибудь комбинаторный злодей написал бы, что это B C или на Хаскель middle = (flip .)
Это так... Шутка.
Да, в Немерле это видно чётче. А что если нам надо функцию от двух аргументов?
f a b c = ...
zipWith (`f` 5) xs ys — здесь (`f` 5) аналогично \a b -> f a 5 b
Как такую функцию описать на Немерле?
VK>А еще partial application в стиле Nemerle позволяет строить member-access выражения. Вроде _.SomeProperty или _.SomeFunc(_), что конечно к каррингу совсем не имеет отношения, но демонстрирует гибкость подхода.
Здравствуйте, Programmierer AG, Вы писали:
PA>Скорее def flip (f) = fun (x,y) { f(y, x) }
Нет, скорее def flip = f => (x,y) => f(y,x)
PA>Кстати, пример очень в тему, показывает, чем отличаются языки с PA>каррингом от языков без .
Nemerle currying и не нужен, равно как и функция flip. Вместо (flip f 2), т.е. flip(f)(2, _) в нотации Nemerle, можно спокойно писать f(_,2).
Ну и скажем f(1,_,2) вместо чего-то вроде:
middle f a c = \b -> f a b c
middle f 1 2
А еще partial application в стиле Nemerle позволяет строить member-access выражения. Вроде _.SomeProperty или _.SomeFunc(_), что конечно к каррингу совсем не имеет отношения, но демонстрирует гибкость подхода.
lomeo,
L>Ух, как красиво. Всё больше убеждаюсь, что пора учить Эрланг. Очень уж он на лисп похож
Подозреваю, что после Хаскеля изучение Эрланга (языка) займёт 2-3 часа . Самое интересное — это библиотеки, и тут только просмотр списка библиотек, входящих в OTP займёт значительное время.
Здравствуйте, ie, Вы писали:
ie>Здравствуйте, VladD2, Вы писали:
VD>>Здравствуйте, Vermicious Knid, Вы писали:
А>>>>> Ну и примитивные применения: map ((*) 2) [1..10], и типа того. Т>>>>Но чтобы поделить на 2 уже придется делать map (flip (/) 2) [1..10] VK>>>Можно просто map (/ 2) [1..10], (/ 2) равнозначно (flip (/) 2).
VD>>Вы бы пояснили смысл сказанного вами. А то боюсь, что многие не поняли что такое flip и зачем он нужен, но спросить стесняются.
ie>Ща попробую, как тут обычно делают
Дополню
J:
flip =: ~
Кстати вызов функции полученной применением наречия ~ (Reverse) с одним аргументом превращается в вызов с двумя, т.е.
Здравствуйте, lomeo, Вы писали:
L>Это все равно, что сказать — в Хаскель flip не нужен, можно спокойно писать (`f` 2)
Нет, не все равно. В Nemerle в отличие от Хаскель есть еще перегрузка по числу параметров и их типам, да еще и параметры по умолчанию. И карринг в духе Haskell или ML там совершенно не к месту. А решение, которое применяется в Nemerle и нагляднее(для всех, кроме комбинаторных злодеев), и не противоречит перегрузке.
L>zipWith (`f` 5) xs ys — здесь (`f` 5) аналогично \a b -> f a 5 b L>Как такую функцию описать на Немерле?
zipWith(f(_,5,_), xs, ys)
Nemerle может и не очень краткий язык, но по крайней мере из этого кода ясно видно, что функция f принимает три параметра, и чтобы это понять не надо знать типы функций zipWith и f.
L>Интересно, можно подробнее?
А о чем тут собственно подробнее? _.SomeMethod(_) превращается в (x,y) => x.SomeMethod(y)
Здравствуйте, ie, Вы писали:
ie>Ниче не понял, но очень интересно! ie>А почему так? Можешь в нескольких словах пояснить?
let fib n = 1;;
у нас определена функция fib
теперь мы её переопределяем
let fib n = if n < 2 then 1 else fib(n-1) + fib(n-2);;
здесь ссылки на старое определение -^----------^
ещё раз переопределяем
let rec fib n = if n < 2 then 1 else fib(n-1) + fib(n-2);;
а здесь те же ссылки уже на вновь определямое значение
Здравствуйте, voxel3d, Вы писали:
V>Объясните, плс, где применяется карринг.
В Haskell, OCaml, лямбда-исчислении.
Вопрос поставлен не совсем правильно. Карринг не применяется для решения каких-то задач. Это просто один из способов представить функцию нескольких аргументов как функцию одного аргумента.
Здравствуйте, lomeo, Вы писали:
L>Надо как то так
L>
L>(define (flip f) (lambda (a b) (f b a)))
L>
Зачем же останавливаться на полпути?
(define (flip f) (lambda (a) (lambda ( b) (f b a))))
L>Поэтому, если в Немерле карринга нет, то там тоже неверно — нельзя сделать flip(f)(b,a)
Высказывание "в языке X карринга нет" довольно таки бессмысленно. Если есть функции высшего порядка, то любую функцию можно определить в каррированной форме.
Здравствуйте, lomeo, Вы писали:
ie>>Теперь ясно. Неясно лишь одно. Для каких нужд может потребоваться такое переопределение. Не черевато ли оно ошибками? L>Это еще фигня. Вот взаимно рекурсивные функции надо объявлять вместе (если я не прав, поправьте плиз).
L>let rec f x = g x
L> and g x = f x;;
Фактически, когда мы пишем на ML
let x = .....
let y = .....
let z = .....
.....
это эквивалентно
let x = ( ..... )
in
let y = ( ..... )
in
let z = ( ..... )
in
( ..... )
end
end
То есть, у нас не один глобальный scope (область видимости имён), а туча вложенных друг в друга.
let rec x = definition() in expression() вводит определяемое имя внутрь области, относящейся к definition(), а просто let — в область expression().
Но из синтаксического сахара это неочевидно!!!
Другое дело — например, Хаскелл, где области видимости большие и зрительно различимые.
x = ... y ... z ... -- это глобальные имена
y = -- всё что ниже и оттабулировано - это вложенная область видимости
... x ... -- поэтому компилятор видит локальный x (а человек понимает, что искать x надо сперва в блоке where, и только потом вовне)where
x = .....
z = ... x ... y ... -- это глобальные имена
Табуляция в Хаскелле — это тоже сахар, но он соответствует всего лишь скобкам, внутри которых — конъюнкция. Коммутативная и ассоциативная, в отличие от let-in.
Объясните, плс, где применяется карринг. На простых примерах. Лучше — на императивном языке или псевдоязыке. В форуме полне постов, где упоминается карринг, но... непонятно.
Что это такое, в принципе, понимаю:
f: (a, b) -> c
карринг сделает:
f: a -> b -> c
Теперь бы понять, зачем оно применяется. И ещё маленький вопрос, тут предполагается, что а и b обязательно функции?
Здравствуйте, <Аноним>, Вы писали:
А> А что, разве не так?
Не так.
А> Вместо одной аксиомы для типа 'a -> 'b нужен набор аксиом для n-арных функций.
Нет тут никаких проблем. Вывод типов усложняется перегрузкой и приведением типов. Но к карингу это отношения не имеет. К тому же есть системы обеспечивающие вывод типов и при этих препятсвиях.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Аноним, Вы писали:
А> Ну и примитивные применения: map ((*) 2) [1..10], и типа того.
Но чтобы поделить на 2 уже придется делать map (flip (/) 2) [1..10].
Здравствуйте, Трурль, Вы писали:
А>> Ну и примитивные применения: map ((*) 2) [1..10], и типа того. Т>Но чтобы поделить на 2 уже придется делать map (flip (/) 2) [1..10]
Можно просто map (/ 2) [1..10], (/ 2) равнозначно (flip (/) 2).
Здравствуйте, Vermicious Knid, Вы писали:
VK>Можно просто map (/ 2) [1..10], (/ 2) равнозначно (flip (/) 2).
Можно. Но это как раз и означает, что просто карринг не всегда удобно. Потребовался дополнительный сахар. Компилятор "знает" про flip, что, вообще говоря, некрасиво.
Здравствуйте, Vermicious Knid, Вы писали:
А>>> Ну и примитивные применения: map ((*) 2) [1..10], и типа того. Т>>Но чтобы поделить на 2 уже придется делать map (flip (/) 2) [1..10] VK>Можно просто map (/ 2) [1..10], (/ 2) равнозначно (flip (/) 2).
Вы бы пояснили смысл сказанного вами. А то боюсь, что многие не поняли что такое flip и зачем он нужен, но спросить стесняются.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Трурль wrote: > L>Поэтому, если в Немерле карринга нет, то там тоже неверно — нельзя сделать flip(f)(b,a) > Высказывание "в языке X карринга нет" довольно таки бессмысленно. Если есть функции высшего порядка, то любую функцию можно определить в каррированной форме.
Это понятно, семантической разницы нет, есть разница синтаксическая,
т.е. все сводится к удобству. Никто не запрещает написать Parsec или
применять монады направо и налево в, скажем, Эрланге, но никто же так не
делает. Или я ошибаюсь, и таки есть библиотеки комбинаторов для языков
без карринга?
Здравствуйте, Трурль, Вы писали:
Т>Высказывание "в языке X карринга нет" довольно таки бессмысленно.
Хорошо, нет автоматического карринга :-)
Хотя, если честно в Хаскеле его тоже нет. Там все функции уже каррированные (звучит как то опасненько).
Просто обычно (хотя и неверно) под каррингом понимается не собственно карринг, а поддержка каррированных функций в языке. В этом смысле это утверждение имеет смысл.
T> Если есть функции высшего порядка, то любую функцию можно определить в каррированной форме.
Здравствуйте, Lazy Cjow Rhrr, Вы писали:
T>>> Если есть функции высшего порядка, то любую функцию можно определить в каррированной форме.
L>>Э-э-э... Тут не понял. Какая связь?
LCR>Считается, что в Эрланге нет карринга:
поскипал
LCR>А оказывается есть: curry — это просто HOF.
Ух, как красиво. Всё больше убеждаюсь, что пора учить Эрланг. Очень уж он на лисп похож :)
LCR>Так что "поддержка карринга" — это просто вопрос о наличии соответствующего синтаксического сахара в языке.
Здравствуйте, Lazy Cjow Rhrr, Вы писали:
LCR>Так что "поддержка карринга" — это просто вопрос о наличии соответствующего синтаксического сахара в языке.
А "язык с каррингом" это язык, у которого + имеет тип int -> int -> int, а не int * int -> int.
Здравствуйте, Vermicious Knid, Вы писали:
L>>Это все равно, что сказать — в Хаскель flip не нужен, можно спокойно писать (`f` 2) VK>Нет, не все равно. В Nemerle в отличие от Хаскель есть еще перегрузка по числу параметров и их типам, да еще и параметры по умолчанию. И карринг в духе Haskell или ML там совершенно не к месту. А решение, которое применяется в Nemerle и нагляднее(для всех, кроме комбинаторных злодеев), и не противоречит перегрузке.
Пардон, я, конечно, говорил не о flip, а о карринге. Вы смотрели Parsec? Как будет выглядеть описание какого нибудь язычка на Немерле без карринга?
На Хаскеле можно писать что то вроде
number = do
s <- many1 digit
return (read s)
expr = simpleExpr <|> addExpr
simpleExpr = number <|> paren
paren = do
char '('
x <- expr
char ')'
return x
и т.д.
Здесь нет ни одной не каррированной функции (ну, кроме return и read), даже определения и те каррированные, благодаря чему описание выглядит понятнее, чем если бы там была куча дополнительных значков, рассказывающих про параметры.
Здесь, где мы с функциями работаем как с комбинаторами нам не интересно знать о параметрах. И, мне кажется, именно в таких случаях каррирование очень выгодно.
L>>zipWith (`f` 5) xs ys — здесь (`f` 5) аналогично \a b -> f a 5 b L>>Как такую функцию описать на Немерле? VK>zipWith(f(_,5,_), xs, ys)
А если теперь так: \a b -> f b 5 a?
VK>Nemerle может и не очень краткий язык, но по крайней мере из этого кода ясно видно, что функция f принимает три параметра, и чтобы это понять не надо знать типы функций zipWith и f
Да-да, очень симпатичный способ. Но если нам как раз не нужно знать о том, сколько параметров принимает функция, если мы работаем с ней как с комбинатором, то каррирование бы очень пригодилось.
L>>Интересно, можно подробнее? VK>А о чем тут собственно подробнее? _.SomeMethod(_) превращается в (x,y) => x.SomeMethod(y)
Здравствуйте, lomeo, Вы писали:
L>Пардон, я, конечно, говорил не о flip, а о карринге.
А я и о flip, и о карринге. В среде где нужно постоянно общаться с библиотеками, написанными на(и для) императивных языках с номинальной системой типов и при этом поддерживать вывод типов, системе типов Хиндли-Милнера не место.
L> Вы смотрели Parsec?
Да, смотрел я и Parsec, и про Packrat читал. И много еще чего, когда интересовался Хаскеллом. Пришел к выводу, что это все игрушки, созданные мечтателями с математическим складом ума. Парсить всего "тысячу строчек за секунду на современных машинах"(это почти цитата из документации по Parsec) это курам на смех, тут нечем гордиться, даже если это цитата десятилетней давности. К тому же я всегда считал, и буду считать дальше, что лучше писать парсеры вручную, используя традиционный алгоритм рекурсивного спуска + по необходимости backtracking.
L> Как будет выглядеть описание какого нибудь язычка на Немерле без карринга?
Как что угодно. Nemerle расширяется через макросы, изменяющие синтаксис или просто параметризованные кусками AST, считывающие поток неразобранных лексем в качестве входных данных, в конце концов можно во время компиляции даже текстовый файл с описанием грамматики считать или вообще вызвать внешний генератор парсеров. Кроме макросов есть определяемые и переопределяемые операторы, неявные преобразования типов, итд, итп.
L>На Хаскеле можно писать что то вроде L>
L>number = do
L> s <- many1 digit
L> return (read s)
L>expr = simpleExpr <|> addExpr
L>simpleExpr = number <|> paren
L>paren = do
L> char '('
L> x <- expr
L> char ')'
L> return x
L>и т.д.
L>
Не очень понятно честно говоря. Этот пример еще ничего, в документации по Parsec есть еще и похуже. Концептуально мне это напомнило прологовские DCG, только мне они почему-то больше нравятся, несмотря на "кучу дополнительных значков":
Это все работает без каких-либо специальных библиотек, только набор простых правил для преобразования DCG в обычные предикаты(они обычно встроены в компилятор). Единственный минус — в отличие от Parsec, который специально создавался для построения парсеров, нет никаких дополнительных функций/предикат типа many1. Поэтому реализация предикаты num выглядит довольно сурово на первый взгляд:
L>Здесь нет ни одной не каррированной функции (ну, кроме return и read), даже определения и те каррированные, благодаря чему описание выглядит понятнее, чем если бы там была куча дополнительных значков, рассказывающих про параметры.
Ну и. А в Прологе функций вообще нет, не то что каррированных. При этом выглядит все весьма понятно. На Nemerle можно написать макрос, который будет строить парсер вообще из BNF или EBNF(кое-что подобное, но не очень удобное уже есть). А можно подумать и сделать что-то отдаленно напоминающее Parsec и без макросов, но не использующее карринг ни в каком виде(все таки в Nemerle есть объекты и определяемые операторы).
L>Здесь, где мы с функциями работаем как с комбинаторами нам не интересно знать о параметрах. И, мне кажется, именно в таких случаях каррирование очень выгодно.
Может быть и выгодно, но выгода это не настолько велика. Вот на OCaml пишется крайне много парсеров, при этом карринг там есть, но в этих парсерах я его осознанного и целенаправленного применения(в смысле всяких комбинаторов итп) не видел.
L>А если теперь так: \a b -> f b 5 a?
Это вроде нельзя(без flip), а смысл? Я вообще-то и partial application, если честно, практически никогда на Nemerle не применял. И в этом случае мне проще написать (a,b) => f(b,5,a), я символы за счет потери читабельности не люблю экономить.
L>Да-да, очень симпатичный способ. Но если нам как раз не нужно знать о том, сколько параметров принимает функция, если мы работаем с ней как с комбинатором, то каррирование бы очень пригодилось.
Нам может и не нужно знать. А вот тому кто это будет потом читать? Да, даже если этим кем-то будешь ты сам, часто лучше оставить себе как можно больше "значков" о том как именно работает этот код.
Здравствуйте, Vermicious Knid, Вы писали:
L>>Пардон, я, конечно, говорил не о flip, а о карринге.
VK>А я и о flip, и о карринге. В среде где нужно постоянно общаться с библиотеками, написанными на(и для) императивных языках с номинальной системой типов и при этом поддерживать вывод типов, системе типов Хиндли-Милнера не место.
Не понял при чём здесь это.
L>> Вы смотрели Parsec?
VK>Да, смотрел я и Parsec, и про Packrat читал. И много еще чего, когда интересовался Хаскеллом. Пришел к выводу, что это все игрушки, созданные мечтателями с математическим складом ума. Парсить всего "тысячу строчек за секунду на современных машинах"(это почти цитата из документации по Parsec) это курам на смех, тут нечем гордиться, даже если это цитата десятилетней давности. К тому же я всегда считал, и буду считать дальше, что лучше писать парсеры вручную, используя традиционный алгоритм рекурсивного спуска + по необходимости backtracking.
Ну и пожалуйста, я не говорю о том, что лучше. Я пытаюсь очертить область применения карринга. Или карринг — это всегда зло?
L>> Как будет выглядеть описание какого нибудь язычка на Немерле без карринга?
VK>Как что угодно. Nemerle расширяется через макросы, изменяющие синтаксис или просто параметризованные кусками AST, считывающие поток неразобранных лексем в качестве входных данных, в конце концов можно во время компиляции даже текстовый файл с описанием грамматики считать или вообще вызвать внешний генератор парсеров. Кроме макросов есть определяемые и переопределяемые операторы, неявные преобразования типов, итд, итп.
Ну вот пожалуйста — одна и та же задача решается каррингом и макросами. Что предпочесть?
VK>Не очень понятно честно говоря. Этот пример еще ничего, в документации по Parsec есть еще и похуже. Концептуально мне это напомнило прологовские DCG, только мне они почему-то больше нравятся, несмотря на "кучу дополнительных значков":
Почему? Если это одно и то же, но в прологовском коде еще и дополнительные параметры у функций?
L>>Здесь нет ни одной не каррированной функции (ну, кроме return и read), даже определения и те каррированные, благодаря чему описание выглядит понятнее, чем если бы там была куча дополнительных значков, рассказывающих про параметры.
VK>Ну и. А в Прологе функций вообще нет, не то что каррированных. При этом выглядит все весьма понятно. На Nemerle можно написать макрос, который будет строить парсер вообще из BNF или EBNF(кое-что подобное, но не очень удобное уже есть). А можно подумать и сделать что-то отдаленно напоминающее Parsec и без макросов, но не использующее карринг ни в каком виде(все таки в Nemerle есть объекты и определяемые операторы).
Ну хорошо. Опять — одна и та же задача решается каррингом и объектами+операторами. Что проще?
L>>Здесь, где мы с функциями работаем как с комбинаторами нам не интересно знать о параметрах. И, мне кажется, именно в таких случаях каррирование очень выгодно.
VK>Может быть и выгодно, но выгода это не настолько велика. Вот на OCaml пишется крайне много парсеров, при этом карринг там есть, но в этих парсерах я его осознанного и целенаправленного применения(в смысле всяких комбинаторов итп) не видел.
Разные есть библиотеки, но я понял.
L>>А если теперь так: \a b -> f b 5 a? VK>Это вроде нельзя(без flip), а смысл? Я вообще-то и partial application, если честно, практически никогда на Nemerle не применял. И в этом случае мне проще написать (a,b) => f(b,5,a), я символы за счет потери читабельности не люблю экономить.
ОК.
L>>Да-да, очень симпатичный способ. Но если нам как раз не нужно знать о том, сколько параметров принимает функция, если мы работаем с ней как с комбинатором, то каррирование бы очень пригодилось. VK>Нам может и не нужно знать. А вот тому кто это будет потом читать? Да, даже если этим кем-то будешь ты сам, часто лучше оставить себе как можно больше "значков" о том как именно работает этот код.
Т.е. "negates xs = filter (\x -> x < 0) xs" лучше, чем "negates = filter (<0)"?
Здравствуйте, Programmierer AG, Вы писали:
PA>ie wrote: >> >> let rec flip f b a = f a b PA>rec лишний
+1
Хорошо, что напомнили По поводу rec давно хотел просвятиться. Итак, что пишут в мануале:
Function definitions may be recursive, provided this is requested explicitly, using the keyword rec:
let rec fib n = if n < 2 then 1 else fib(n-1) + fib(n-2);;
А просвятиться хотел собственно в таком аспекте: нахрена этот rec нужен? (небыло бы я бы и не написал лишний rec )
Хинт компилятору о том что будут рекурсивные вызовы? Да нет, конечно, типы выводить умеем, а распознать рекурсию не сможем --- смешно.
Декларация разработчика о намерениях? Тоже, ИМХО, бред. На всех языках пишут рекурсии и как-то без rec понимают, что идут рекурсивные вызовы.
Скорее всего, тут что-то другое. Но вот что?!
ie>А просвятиться хотел собственно в таком аспекте: нахрена этот rec нужен?
# let fib n = 1;;
val fib : 'a -> int = <fun>
# fib 8;;
- : int = 1
# let fib n = if n < 2 then 1 else fib(n-1) + fib(n-2);;
val fib : 'a -> int = <fun>
# fib 8;;
- : int = 2
# let rec fib n = if n < 2 then 1 else fib(n-1) + fib(n-2);;
val fib : int -> int = <fun>
# fib 8;;
- : int = 34
Здравствуйте, Трурль, Вы писали:
ie>>А просвятиться хотел собственно в таком аспекте: нахрена этот rec нужен? Т>
Т># let fib n = 1;;
Т>val fib : 'a -> int = <fun>
Т># fib 8;;
Т>- : int = 1
Т># let fib n = if n < 2 then 1 else fib(n-1) + fib(n-2);;
Т>val fib : 'a -> int = <fun>
Т># fib 8;;
Т>- : int = 2
Т># let rec fib n = if n < 2 then 1 else fib(n-1) + fib(n-2);;
Т>val fib : int -> int = <fun>
Т># fib 8;;
Т>- : int = 34
Т>
Ниче не понял, но очень интересно!
А почему так? Можешь в нескольких словах пояснить?
Здравствуйте, VladD2, Вы писали:
L>>Т.е. "negates xs = filter (\x -> x < 0) xs" лучше, чем "negates = filter (<0)"?
VD>Чтобы не трепаться в пустую... Вот кусок кода который я написал вчера для проекта интеграции: VD>
VD>foreach ((name, lst) when lst.Exists(_ is MethodBuilder)in _extension_methods.KeyValuePairs)
VD> ...
VD>
VD>"_ is MethodBuilder" превращается в нужную фукнцию. Точно так же вместо твоего negates можно просто написать "filter(_ < 0)".
Здравствуйте, Трурль, Вы писали:
ie>>Ниче не понял, но очень интересно! ie>>А почему так? Можешь в нескольких словах пояснить? Т>
Т> let fib n = 1;;
Т>у нас определена функция fib
Т>теперь мы её переопределяем
Т> let fib n = if n < 2 then 1 else fib(n-1) + fib(n-2);;
Т>здесь ссылки на старое определение -^----------^
Т>ещё раз переопределяем
Т> let rec fib n = if n < 2 then 1 else fib(n-1) + fib(n-2);;
Т>а здесь те же ссылки уже на вновь определямое значение
Т>
Теперь ясно. Неясно лишь одно. Для каких нужд может потребоваться такое переопределение. Не черевато ли оно ошибками?
Здравствуйте, VladD2, Вы писали:
VD>В обещм, вопрос как всегда в терминах. Что есть тяжелая артилерия, а что мирный атом.
Я не берусь утверждать, т.к. опыта использования макросов практически нет, но мне кажется, что микро-DSL на одних лишь HOF требуют меньше кода.
Здравствуйте, Lazy Cjow Rhrr, Вы писали:
LCR>Подозреваю, что после Хаскеля изучение Эрланга (языка) займёт 2-3 часа :-). Самое интересное — это библиотеки, и тут только просмотр списка библиотек, входящих в OTP займёт значительное время.