LINUX.ORG.RU
ФорумTalks

лямбды в новых язычках - PR или реальные полезняшки?

 , ,


7

7

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

Ну что есть lambda в каком-нибудь lisp я представляю и даже понимаю зачем оно и как им пользоваться. В lisp'е. А что имеется ввиду под «лямбдой» например, в C#?

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

Только чтобы это не было аналогом перлового однострочника типа

perl -e 'print sub{ $_[0] + $_[1]; }->(1,2)."\n";'
ибо в этом никаких новшеств и преимуществ нету.

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

★★★★★
Ответ на: комментарий от Xellos

Пять раз прочёл, не понял, почему. Откуда F знает что такое x?

Ей насрасть. Дали некий код - оно его на место сунуло.

Типа сишного #define A(x) ( x + y ) - вместо x можно что угодно пихать, в итоге при компиляции получим код вида ( «впихнутое» + y ).

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

Вот так с полпинка не знаю, как такое на перле написать...

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

Вот пример на Питоне:

А можно на питоне написать типа:

g(n) = lambda x: ( x + n )
f = g(1/x)
print f(2)
?

Или вся лямбдность ограничивается только тем, что есть переменная f в которой грубо говоря хранится «адрес функции с одним аргументом которая возвращает аргумент увеличенный на 1» ? Понятно, что можно написать g = f и print g(2) напишет 3, или там передать f как параметр к какой-нибудь функции дергающей callback.

Stanson ★★★★★
() автор топика
Ответ на: комментарий от tailgunner
F(n) = lambda (x)->(x+n)
a(x) = F(sin(1/x))

F - функция от одного параметра x, она возвращает функцию, которая тоже от одного параметра и добавляет к своему аргументу то значение, которое было передано в качестве x в функцию F.

a - функция от одного параметра x, которая возвращает функцию, которая будет добавлять к своему аргументу значение sin(1/x)

Если вам путают x, то эквивалентно

F(n) = lambda (x)->(x+n)
a(q) = F(sin(1/q))
vertexua ★★★★★
()
Ответ на: комментарий от geekless

Распарсил с 5-й попытки, что n имеет тип lambda (x).

Не обязательно. :)

Что это за язык? Или псевдокод?

Псевдокод, чтобы избежать языкосрача.

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

Распарсил с 5-й попытки, что n имеет тип lambda (x).

Вообще нет, почему? Я вообще не знаю что это за тип. Тут непонятно, динамика это или type inference

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

F(n) = lambda (x)->(x+n)

Распарсил с 5-й попытки, что n имеет тип lambda (x).

По-моему, единственное, что мы знаем об этой переменной - то, что ее можно складывать (что исключает тип «функция») %)

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

После твоего фикса баг не только не исчез, но и вообще все упало и больше не запускается. Более правильным фиксом было бы убрать «лямбда в ».

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

Не обязательно. :)

Да, теперь вижу, что распарсил непрапвильно. Ты меня изначально несколько запутал своим a(b) примером.

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

Ну в общем в перле тоже можно, как подсказывает нам Sub::Lambda.

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

А можно на питоне написать типа:

Если интерпретация vertexua верна, то да, в Питоне так можно.

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

Откуда во второй строчке возьмется x?

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

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

Как это любая переменная? Она определена прежде? Или это такой синтаксис лямбд хитрый в вашем воображаемом языке где x дефолтное значение параметра?

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

Как это любая переменная? Она определена прежде? Или это такой синтаксис лямбд хитрый в вашем воображаемом языке где x дефолтное значение параметра?

Плюсую вопрос.

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

Да не в переменной суть.

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

Ну как в стандартном лисповском примере:

    (define (doubler f) (lambda (x) (f x x)))
    ((doubler +) 4)

Можно написать (doubler *) и получим функцию возведения в квадрат. Аргументом передаётся не значение и не адрес функции даже (как было бы в C, например) а действие.

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

Ну такое кроме почти все умеют.

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

Так-то можно и сями с #define обойтись и в этом ничего нового не будет. А вот генерация кода, как мечтают функциональщики, была бы весьма интересна.

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

Обычно создается функциональный обьект в рантайме который инкапсулирует замкнутые знанчеия. Не совсем понял ваш вопрос. Но это не define.

Генерация кода из другой оперы, man мкаросы лиспа

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

кстати вот про это «инкапсулирует замкнутые значения» забавно. Если язык гарантирует отсутствие нулей, то что делать, когда замыкаются указатели на объекты, а не их значения. Т.е. сделать-то это можно, но тогда у garbage collector'а возникаю ацкие проблемы. И еще, что делать с concurrency этих бедных замкнутых объектов. Особенно там, где объект неизвестен на этапе конпеляции, и его описание рождается действительно динамически (трейты, миксины, eval'ы, подгрузка внешнего байткода, итп)

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

Генерация кода из другой оперы, man мкаросы лиспа

Один из способов реализации вложенных функций (замыканий) - генерация небольшого исполняемого кода (trampoline) в рантайме.

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

Так в чем проблема-то? Словарь инкапсулированных объектов принципиально не отличается от словаря локального фрейма или экземпляра класса.

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

когда замыкаются указатели на объекты, а не их значения. Т.е. сделать-то это можно, но тогда у garbage collector'а возникаю ацкие проблемы

Почему?

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

то что можно тихо и незаметно утянуть за собой в замыкание половину рантайма, и тот сайт во вкладочке который должен был отжирать 80 мегабайт, внезапно стал отжирать 160 мегабайт, а через день - уже 800, а потом в толксах будут писать что «хромиум тормозит»

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

Один из способов реализации вложенных функций (замыканий) - генерация небольшого исполняемого кода (trampoline) в рантайме.

Если мы делаем замыкание в JS, а потом всё это JIT-компилируется, где тут отличить одно от другого? :}

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

Генерация кода из другой оперы, man мкаросы лиспа

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

Т.е. если некий новый язык программирования умеет те же лямбды как их описывают функциональщики - «возвращает новую функцию», то это без сомнения круто. А если это недалеко ушло от #define - то какого чорта вообще упоминать про «лямбды», коих «лямбд» в любом языке хоть жопом жуй?

Или я функциональщиков слишком буквально воспринимаю и вообще все существующие реализации лямбд по сути анонимные функции или генераторы типа #define?

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

Ну это можно и без замыканий легко и просто устроить. :-D

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

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

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

ну вот прикинь, делает человек сайтег и в глобальных переменных сделал какой-нибудь мап на 500 метров. Например, там хранятся геометрические размеры кнопочек. А потом юзает этот мап во всех замыканиях подряд (аргумент какого-нибудь обработчика нажатия кнопочки, типа когда кнопочка нажатая ее цвет с map(default_color) меняется на map(pressed_color)). И все бы хорошо, но тут умный конпелятор замечает, что какой-то поток параллельно пишет в этот мап (читает в него длины кнопочек аяксом из xml-веб-сервиса), и чтобы не нарушить потоковую целостность начинает копировать этот мап по значению в новую внутреннюю переменную. Хопа, теперь, пусть временно, наша шляпа разрослась минимум в 2 раза (если конпелятор сделал всего одну модифицированную версию спецом для того треда который меняет, а если для всех?). Что-то там заоптимизирует движок физического уровня, что-то ужмет ось, но память явно просрется.

фантазирую, ибо в жс пока нет нормальных тредов и я не знаю, как там оно по-настоящему устроено, но все же...

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

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

Нет.

Func<int, int, int> sum = (x, y) => x + y;
х.з. как правильно, но типа где-нибудь при каком-нибудь условии
{
  Func<int, int> f = sum(x, 100*sin(1/x));
  int a = f(2);
}

Понятно, что синус с целочисленными - дурдом, но чисто для примера. Вместо 100*sin(1/x) может быть что угодно - хоть usb_control_msg() какое-нибудь, а вместо sum - что-нибудь, например, обрабатывающее ошибки включая EAGAIN и пр.

И самый интересный вопрос - если условие не выполнено, то код f(x){ return x + sin(1/x)} не существует. А если выполняется, то он рождается при вызове строчки с Func<int, int>

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

фантазирую

Вот именно. Как уже сказано, activation record функции, сохраняемая при создании замыкания - это такой же объект, как и любой другой, и геморроя сборщику мусора не добавляет (программисту - может).

конпелятор

Уровень рассуждения соответствует используемым терминам.

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

Новые функции тоже без проблем:

Func<int, Func<int, int>> currSum = x => y => x + y;
int a = currSum(5)(6); // a == 11
var plusAdin = currSum(1);
int b = plusAdin(10); // b == 11

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

расскажи подробней, ты в замыкание собрался по значению принудительно скопировать все зависимости из окружения что ли, и потом гордишься что оно теперь неизменяемое потому что мы на одно сраное замыкание утащили из рантайма копию объектов в 500 метров весом? =) Или прямо наоборот - сохранить только референсы на объекты из окружения и вообще ничего с собой не тащить (что может привести к проблемам с concurrency и ссылочной целостностью)?

программисту - может

это одно и то же, программист-пользователь не должен знать деталей реализации, он просто пользователь

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

Вот например такое, на сях, чтоб понятно было что делает и зачем нужно:

#define AGAIN_HANDLER(x) \
({\
  while(1){\
    int ret = (x);\
    if( ret != -EAGAIN ) break;\
  }\
ret;\
})

...

где мне нужно
if(type == BULK)
  ret = AGAIN_HANDLER(usb_bulk_read(dev,ep,buf,size,1000));
else
  ret = AGAIN_HANDLER(usb_interrupt_read(dev,ep,buf,size,1000));

При этом, код в #define - это типа лямбда. AGAIN_HANDLER - функция, которая возвращает функцию. Вызов AGAIN_HANDLER рождает код, который сделан из типа лямбды и функции-аргумента для AGAIN_HANDLER.

Понятно, что в сях это будет на этапе компиляции подставлено и всё. Но работать и выглядеть будет по-лямбдски типа. И наверно можно даже вопить везде, что в C есть лямбды. Но на самом деле лямбдами не пахнет, а имеется простой #define.

Точно так же и в примере - на сях это будет просто

#define sum(x,y) ( (x) + (y) )
...
int res = sum(5,6);

Отчего лямбда в вашем примере типа есть, а в сях её нету? То же самое всё, и результат, и фичи. В сях даже фич побольше будет.

Stanson ★★★★★
() автор топика

Безымянные функции всегда удобны. Можно и без них, но ощущения не те

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

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

Ты сам-то помнишь, о чем говорил?

stevejobs> что делать, когда замыкаются указатели на объекты

Впрочем, указатели или значения замыкаются - работу сборщика мусора это не осложняет. Или, по крайней мере, ты не объяснил, откуда возьмутся сложности.

tailgunner ★★★★★
()
Ответ на: комментарий от Stanson
def eagainHandler(&x)
    lambda {
        loop {
            result = x.call
            return result if result != EAGAIN
        }
    }
end


ret = if type == BULK
    eagainHandler{usb_bulk_read(dev,ep,buf,size,1000)}
else
    eagainHandler{usb_interrupt_read(dev,ep,buf,size,1000)}
end

Оно?

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