Лямбда-выражения: зачем и почему
От: J_K  
Дата: 07.12.10 01:19
Оценка:
Здравствуйте!
Объясните пожалуйста, на пальцах, зачем использовать лямбда-выражения, с примерами настоящего использования.
Который день пытаюсь разобраться, но смысл ускользает.
Расщепление кода на методы — это понятно, для удобства чтения, отладки и т.д. С лямбда-выражениями как будто наоборот — мы в коде указываем методы, которые должна выполнить наша функция. От этого код становится ну совершенно нечитаемым, но может, у меня плохие примеры. Дайте тогда хорошие.
Спасибо!
Life is very short and there's no time
for fussing and fighting... (C) Paul McCartney & John Lennon
Re: Лямбда-выражения: зачем и почему
От: Аноним  
Дата: 07.12.10 02:00
Оценка: 1 (1)
Здравствуйте, J_K, Вы писали:

J_K>Здравствуйте!

J_K>Объясните пожалуйста, на пальцах, зачем использовать лямбда-выражения, с примерами настоящего использования.
J_K>Который день пытаюсь разобраться, но смысл ускользает.
J_K>Расщепление кода на методы — это понятно, для удобства чтения, отладки и т.д. С лямбда-выражениями как будто наоборот — мы в коде указываем методы, которые должна выполнить наша функция. От этого код становится ну совершенно нечитаемым, но может, у меня плохие примеры. Дайте тогда хорошие.
J_K>Спасибо!

Это надо покурить ФП и понять, что функция есть первоклассное значение, кстати. И осознать некоторые приемы из ФП, которые несколько могут упростить жизню.
Вон Nemerle, не к ночи будь помянут, можно покурить)

Т.е., есть функции, которые должны получать не только данные, но и действия, определяемые пользователем.
Например, функция сортировки в качестве аргумента может получать делегат, который и указывает как именно надо сравнивать объекты. Проблема в том, что если сортировка по заданному критерию используется только один раз, то объевление отдельно от места использования сортировки функцию сравнения дает только мусор, так как больше оно нигде не используется. А так код находится в месте использования.
Re: Лямбда-выражения: зачем и почему
От: Sinix  
Дата: 07.12.10 02:17
Оценка:
Здравствуйте, J_K, Вы писали:

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


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


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

И да, само существование лямбд не означает, что они — единственный тру-инструмент, и что их надо использовать везде где можно и нельзя

Еслиинтересует матчасть — http://blogs.msdn.com/b/ericlippert/archive/2007/01/10/lambda-expressions-vs-anonymous-methods-part-one.aspx и по тегу.
Re: Лямбда-выражения: зачем и почему
От: _FRED_ Черногория
Дата: 07.12.10 05:16
Оценка: +1
Здравствуйте, J_K, Вы писали:

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

J_K>Который день пытаюсь разобраться, но смысл ускользает.
J_K>Расщепление кода на методы — это понятно, для удобства чтения, отладки и т.д. С лямбда-выражениями как будто наоборот — мы в коде указываем методы, которые должна выполнить наша функция. От этого код становится ну совершенно нечитаемым, но может, у меня плохие примеры. Дайте тогда хорошие.

Выносить общие участки кода в именованные методы, конечно, тоже хорошо. Но, если таких методов нужно штук несколько и использоваться они будут внутри только одного метода, то это может затруднить редактирование этого одного метода. А с лямбдами (анонимными методами) — "сохранил" метод в локальной переменной и используй. Метод больше не нужен — удаляй переменную и изменился, с точки зрения класса, только один метод, а не сразу несколько.

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

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

Конкретно лямбды (а не вообще анонимные функции) так же удобны для построения Expression Tree — специального объекта Expression, содержащего в себе "дерево выражений".

Вообще, откройте для себя раздел статьи на этом сайте. В боьшинстве статей будет рассказано про анонимные методы/ламбды и то, где оих удобно использовать.
Help will always be given at Hogwarts to those who ask for it.
Re: Лямбда-выражения: зачем и почему
От: Sinclair Россия https://github.com/evilguest/
Дата: 07.12.10 05:43
Оценка: 19 (2)
Здравствуйте, J_K, Вы писали:
J_K>Расщепление кода на методы — это понятно, для удобства чтения, отладки и т.д. С лямбда-выражениями как будто наоборот — мы в коде указываем методы, которые должна выполнить наша функция. От этого код становится ну совершенно нечитаемым, но может, у меня плохие примеры. Дайте тогда хорошие.
Расщепление кода на методы имеет смысл тогда, когда
а) название метода получается короче его тела и
б) есть надежда использовать его более одного раза.
Вот, допустим, надо тебе отсортировать (в смысле упорядочить) коллекцию сложных объектов по некоторому ключу:
ISomething<T> Sort(this ISomething<T> source, Function<T, K> key)
  where K: IComparable
{}

Ну и вот, допустим, хотим мы отсортировать Person по его LastName. Как будет устроен метод для передачи в аргумент key — понятно, return person.LastName. А как его назвать? PersonLastNameRetriever? Уже длиннее текста.
В этом случае очевидно, что лямбда будет читаться лучше, т.к. не надо будет бегать смотреть, что там именно делает этот PersonLastNameRetriever:
persons = persons.Sort(p=>p.LastName);

Это было пояснение по поводу пункта а.
Теперь по поводу пункта б).
Предположим, ты хочешь сделать так, чтобы некоторый код умел повторно выполняться несколько раз.
Ну, то есть логика такая, что если возникло исключение при выполнении какого-то фрагмента, то хочется чуть-чуть подождать и попробовать ещё пару раз — может, там коннект неустойчивый. Запрограммировать такое нетрудно, но если задачка встречается часто, захочется завернуть её в готовую функцию. И вот у тебя получается что-то типа
public void TryMultiple(int retryCount, Action a)

Вот как правило внутрь этого TryMultiple ты будешь передавать методы, которые никогда больше не вызовешь. Точно так же, как ты вряд ли захочешь выделить тело какого-то произвольного цикла в отдельный метод, так и тут — нагляднее встраивать код по месту:

TryMultiple(3, ()=>
{
  // download the file;
});
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: Лямбда-выражения: зачем и почему
От: J_K  
Дата: 07.12.10 06:15
Оценка:
Спасибо всем за ссылки и разъяснения! Не все еще понятно... например, получается, что эти лямбды удобно использовать только когда очень маленькие кусочки кода повторяются? Потому что с большими код явно быстро станет нечитаемым..
Life is very short and there's no time
for fussing and fighting... (C) Paul McCartney & John Lennon
Re[3]: Лямбда-выражения: зачем и почему
От: _FRED_ Черногория
Дата: 07.12.10 06:18
Оценка:
Здравствуйте, J_K, Вы писали:

J_K>Спасибо всем за ссылки и разъяснения! Не все еще понятно... например, получается, что эти лямбды удобно использовать только когда очень маленькие кусочки кода повторяются? Потому что с большими код явно быстро станет нечитаемым..


Вовсе не обязательно. Что именно мешает "читабельности" в лямбдах
Help will always be given at Hogwarts to those who ask for it.
Re: Функциональные литералы
От: Qbit86 Кипр
Дата: 07.12.10 06:36
Оценка: -1
Здравствуйте, J_K, Вы писали:

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


Вначале сформулируй для себя ответ на вопрос, зачем использовать в коде числовые литералы. Например, можно ли писать «var x = 1.0 / y;», или надо писать «var x = m_one / y;», где m_one определено как член класса и доступно всем его методам.

Есть и более экзотические причины использования лямбд, например, ограничение скоупа локальных переменных.
Глаза у меня добрые, но рубашка — смирительная!
Re[2]: Функциональные литералы
От: Ziaw Россия  
Дата: 07.12.10 17:32
Оценка: +1
Здравствуйте, Qbit86, Вы писали:

Q>Есть и более экзотические причины использования лямбд, например, ограничение скоупа локальных переменных.


Господи, ну и извращения Автор накушавшись нормальных языков эмулирует их возможности, которые без нужного сахара делают код:

  1. (-) сильно хуже читаемым, реально понять паттерн можно только прочитав/написав его раз двадцать, а место в голове не резиновое.
  2. (-) менее быстрым (вероятно)
  3. (+) чуть более строгим к ошибкам определенного класса и

Что здесь перевесит — каждые решает для себя сам, главное не сильно увлекаться, keep it simple.

Впрочем Let вполне читаем, не очень понятно только, как передавать более одного результата, что будет с кодом, когда в заказчик поменяет задание и в исходной формуле появится зависимость не только от sin_φ но и от φ. Конечно можно выкрутиться на анонимных объектах или рекурсивных Let, но читабельность отобьет напрочь.
Re[3]: Let it be
От: Qbit86 Кипр
Дата: 07.12.10 22:14
Оценка:
Здравствуйте, Ziaw, Вы писали:

Z>Господи, ну и извращения :) Автор накушавшись нормальных языков эмулирует их возможности, которые без нужного сахара делают код:

Z>(−) сильно хуже читаемым, реально понять паттерн можно только прочитав/написав его раз двадцать, а место в голове не резиновое.
Z>(−) менее быстрым (вероятно)
Z>(+) чуть более строгим к ошибкам определенного класса

Всё верно.

Z>Что здесь перевесит — каждые решает для себя сам, главное не сильно увлекаться, keep it simple.


Я и решил — не сильно увлекаться. Не помню, чтоб я где-то в рабочем коде подобное использовал. Разве что Let() из Reactive Extensions пару раз.

Z>Впрочем Let вполне читаем,


Даже это сомнительно.

Z>не очень понятно только, как передавать более одного результата,


Добавить для удобства перегрузку Let(), принимающую кортеж — обновил пост.

Z>что будет с кодом, когда в заказчик поменяет задание и в исходной формуле появится зависимость не только от sin_φ но и от φ.


Вложенные Let(). Предыдущее «φ» будет не «забываться», а замыкаться.
Глаза у меня добрые, но рубашка — смирительная!
Re[2]: Лямбда-выражения: зачем и почему
От: Tom Россия http://www.RSDN.ru
Дата: 07.12.10 23:53
Оценка:
S>б) есть надежда использовать его более одного раза.
То есть если метод будет используется только один раз и вызывается только из одного места — то не имеет смысла заводить такой метод
Народная мудрось
всем все никому ничего(с).
Re[2]: Лямбда-выражения: зачем и почему
От: Tom Россия http://www.RSDN.ru
Дата: 07.12.10 23:54
Оценка: -1
_FR>Выносить общие участки кода в именованные методы, конечно, тоже хорошо. Но, если таких методов нужно штук несколько и использоваться они будут внутри только одного метода, то это может затруднить редактирование этого одного метода.
Всё как раз наабарот. Это значительно позволит упростить чтение и редактирование метода
Народная мудрось
всем все никому ничего(с).
Re[3]: Лямбда-выражения: зачем и почему
От: Sinclair Россия https://github.com/evilguest/
Дата: 08.12.10 06:56
Оценка: +4
Здравствуйте, Tom, Вы писали:

S>>б) есть надежда использовать его более одного раза.

Tom>То есть если метод будет используется только один раз и вызывается только из одного места — то не имеет смысла заводить такой метод
Как правило — да.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: Лямбда-выражения: зачем и почему
От: _FRED_ Черногория
Дата: 08.12.10 07:18
Оценка:
Здравствуйте, Tom, Вы писали:

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

Tom>Всё как раз наабарот. Это значительно позволит упростить чтение и редактирование метода

Нет, потому что если вынесенный метод окажется не нужным и понадобится пара других, то придётся редактировать что-то вне одного метода, затронув ещё несколько [вынесенных] методов.
Help will always be given at Hogwarts to those who ask for it.
Re[3]: Лямбда-выражения: зачем и почему
От: _FRED_ Черногория
Дата: 08.12.10 07:39
Оценка:
Здравствуйте, Tom, Вы писали:

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

Tom>Всё как раз наабарот. Это значительно позволит упростить чтение и редактирование метода

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

А так, конечно, сформулировать какое-то чёткое правило сложно. Результат (вынес — не вынес) — есть причина того, что сейчас в голове и что, по ощущениям, может быть в голове в будущем
Help will always be given at Hogwarts to those who ask for it.
Re[4]: Let it be
От: Ziaw Россия  
Дата: 08.12.10 18:42
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Добавить для удобства перегрузку Let(), принимающую кортеж — обновил пост.


А почему не анонимный объект? Перегрузки бы не понадобились. Имхо, кортежи без ПМ и вывода типов унылы и полезны только при передаче между "глобальными" методами.

public static double LatitudeToY(double latitude)
{
    return new {
            maxPixelsCount = (Math.PI / 180.0)
                .Let(radiansPerDegree => latitude * radiansPerDegree)
                .Let(φ => Math.Sin(φ))
                .Let(sin_φ => 0.5 * Math.Log((1.0 + sin_φ) / (1.0 - sin_φ)))
                .Let(y => 0.5 * (1.0 - y / Math.PI)),
            normalizedY = 2147483648.0  
        }.Let(comp => comp.maxPixelsCount * comp.normalizedY);
}

Получилось слегка читабельнее кортежа, имхо.

Z>>что будет с кодом, когда в заказчик поменяет задание и в исходной формуле появится зависимость не только от sin_φ но и от φ.


Q>Вложенные Let(). Предыдущее «φ» будет не «забываться», а замыкаться.


+1. Я это и имел ввиду когда писал о рекурсивных вызовах, сори за корявый термин. Упомянул я об этом варианте к тому, что слегка изменить код не получится.

Впрочем если это просто этюд, никаких претензий.
Re[5]: Этюд
От: Qbit86 Кипр
Дата: 08.12.10 19:57
Оценка:
Здравствуйте, Ziaw, Вы писали:

Q>>Добавить для удобства перегрузку Let(), принимающую кортеж — обновил пост.

Z>А почему не анонимный объект? Перегрузки бы не понадобились.

Во-первых, перегрузка и в случае кортежа не обязательна. Просто хотелось, чтобы привязка (I mean «let-binding») выглядела так:
(maxPixelsCount, normalizedY) => maxPixelsCount * normalizedY

а не так:
comp => comp.maxPixelsCount * comp.normalizedY

или так:
maxPixelsCountAndNormalizedY => maxPixelsCountAndNormalizedY.Item1 * maxPixelsCountAndNormalizedY.Item2


Во-вторых, не было цели любой ценой сымитировать на C# подобие функционала из «нормальных языков». Скорее, хотелось проиллюстрировать тот факт, что let и where — это просто синтаксический сахар поверх обычного (типизированного) лямбда-исчисления:
let x : σ = N in M ≝ (λx : σ. M) N
C#-specific «спецсредства» вроде анонимных классов в этом отношении скорее мешали бы, хотелось некоторой «чистоты», евпочя. И так пришлось прибегнуть к extension-методам, иначе совсем грустно смотрелось бы.

А так да, имитация let через анонимные классы удобна, например, если зачем-то понадобится переписать LINQ-запрос, записанный в query comprehension syntax с использованием let, на fluent-синтаксис вызовов методов Select, Where, Aggregate, etc.

Z>Имхо, кортежи без ПМ и вывода типов унылы


Именно это я и имел в виду, посетовав на отсутствие возможности «деконструирования» кортежа (которое осуществляется посредством паттерн-мэтчинга).

Z>Впрочем если это просто этюд, никаких претензий.


Всё верно, просто этюд под впечатлением от книги Джона Митчелла «Основания языков программирования».
Глаза у меня добрые, но рубашка — смирительная!
Re[4]: Лямбда-выражения: зачем и почему
От: Tom Россия http://www.RSDN.ru
Дата: 08.12.10 20:02
Оценка: -1 :))
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Tom, Вы писали:


S>>>б) есть надежда использовать его более одного раза.

Tom>>То есть если метод будет используется только один раз и вызывается только из одного места — то не имеет смысла заводить такой метод
S>Как правило — да.
Только у тех кто НЕ пишет юнит тестов и НЕ соблюдает single responsibility pronciple, но как правило да, на пост советском пространстве эти, как и другие принципы SOLID, не соблюдаются что приводит к очень низкому качеству кода
Народная мудрось
всем все никому ничего(с).
Re[4]: Лямбда-выражения: зачем и почему
От: Tom Россия http://www.RSDN.ru
Дата: 08.12.10 20:05
Оценка:
_FR>Это от размера метода и ["важности"] решаемой им задачи сильно зависит. Если такой метод решает довольно не тривиальную задачу и [субъективно] есть возможность изменить его логику, не затрагивая метод, в котором первый используется, то я бы вынес.
Ерунда это несусветная. Важность либо что то ещё никак не влияет на решение выносить что то в отдельный метод или класс или нет. Единственное что на это влияет — это Single Responsibility Principle. Если метод имеет несколько responsibility то его надо разбивать не учитывая сколько раз и откуда будут вызываться новые методы. Их может быть хоть 20 и все могут использоваться только из одного места, главное что бы каждый метод имел только одну responsibility.

_FR>А так, конечно, сформулировать какое-то чёткое правило сложно. Результат (вынес — не вынес) — есть причина того, что сейчас в голове и что, по ощущениям, может быть в голове в будущем

Принципы хорошего дизайна не надо формулировать, их уже сформулировали. Остаётся им только следовать.
Народная мудрось
всем все никому ничего(с).
Re[5]: Принцип локальности
От: Qbit86 Кипр
Дата: 08.12.10 20:10
Оценка:
Здравствуйте, Tom, Вы писали:

S>>>>б) есть надежда использовать его более одного раза.

Tom>>>То есть если метод будет используется только один раз и вызывается только из одного места — то не имеет смысла заводить такой метод :no: :???:
S>>Как правило — да.
Tom>Только у тех кто НЕ пишет юнит тестов и НЕ соблюдает single responsibility pronciple, но как правило да, на пост советском пространстве эти, как и другие принципы SOLID, не соблюдаются что приводит к очень низкому качеству кода

Вынесение одноразового метода нарушает принцип локальности. Есть риск, что кто-то из команды тоже станет использовать твой метод, и в дальнейшем ты не сможешь его изменить, не затронув чужой код. Загрязняет пространство имён, вывод intellisense.
Глаза у меня добрые, но рубашка — смирительная!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.