Что это за «лямбда», о которой все говорят?

93

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

Может ли кто-нибудь объяснить мне его истинную ценность?

Джош Хант
источник
16
Могу я указать респондентам, что спрашивающий нигде не упоминает .net
1
проверьте этот связанный qn stackoverflow.com/questions/16501/what-is-a-lambda-function
yesraaj
Ответный вопрос. Спасибо за вопрос.
арахис
Лямбды относятся к миру функционального программирования (декларативного программирования).
RBT

Ответы:

179

Функции без имени

Проще говоря, лямбда - это функция без имени или анонимная функция. Небольшой фрагмент исполняемого кода, который можно передавать, как если бы это была переменная. В JavaScript:

function () {}; // very simple

Давайте теперь посмотрим, как использовать эти лямбды.

Абстрагирование шаблонного кода

Лямбды можно использовать для абстрагирования шаблонного кода. Например петли. Мы привыкли писатьfor и whileзацикливаться целый день. Но это код, который не пишется. Мы могли бы извлечь код внутри цикла, наиболее важной части цикла, и абстрагироваться от остального:

for (var i=0; i<array.length; i++) {
    // do what something useful with array[i]
}

используя forEach объектов массива становится:

array.forEach(function (element, index) {
   // do something useful with element
   // element is the equivalent of array[i] from above
});

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

var numbers = [1, 2, 3, 4];
var even    = [];

// keep all even numbers from above array
for (var i=0; i<numbers.length; i++) {
    if (numbers[i] % 2 === 0) {
        even.push(numbers[i]);
    }
}

alert(even);

// Using the filter method
even = [1, 2, 3, 4].filter(function (number) {
    return number % 2 === 0;
});

alert(even);

Задержка выполнения кода

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

window.onload = function () {
    alert("Loaded");
};

window.setTimeout(function () {
    alert("Code executed after 2 seconds.");
}, 2000);

Это можно было сделать и другими способами, но они довольно многословны. Например, в Java естьRunnable интерфейс.

Фабрики функций

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

var users = [];
var getUser = function (name) {
    if (! users[name]) {
        // expensive operations to get a user. Ajax for example
        users[name] = user_from_ajax;
    }

    return users[name];
};

Позже мы можем заметить, что у нас есть похожая функция:

var photos = [];
var getPhoto = function (name) {
    if (! photo[name]) {
        // expensive operations to get a user. Ajax for example
        photos[name] = photo_from_ajax;
    }

    return photos[name];
};

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

/**
 * @param {Array}     store Data structure in which we cache lambda's return values
 * @param {Function}  lambda
 * @return {Function} A function that caches the result of calling the lambda param
 */
var memoize = function (store, lambda) {
    // return a new lambda
    return function (name) {
        if (! store[name]) {
            // Execute the lambda and cache the result
            store[name] = lambda(name);
        }

        return store[name];
    };
};

var getUsers = memoize([], function (name) {
    // expensive operations to get a user. Ajax for example
});

var getPhotos = memoize([], function (name) {
    // expensive operations to get a photo. Ajax for example
});

Как видите, с помощью лямбда-выражений мы смогли абстрагироваться от логики кеширования / запоминания. Если бы для другого примера были какие-то обходные пути, я считаю, что эту конкретную проблему вряд ли можно решить с помощью других методов. Нам удалось собрать важный шаблонный код в одном месте. Не говоря уже о том, что мы избавились отusers и photosглобальных переменных.

Глядя на ваш профиль, я вижу, что вы в основном пользователь Python. Для вышеприведенного шаблона в Python есть концепция декораторов. В сети есть множество примеров для декораторов мемоизации . Единственная разница в том, что в Python у вас, скорее всего, есть именованный вложенная функция внутри этой функции-декоратора. Причина в том, что Python поддерживает только лямбды с одним выражением. Но концепция та же.

В качестве примера использования лямбда-выражения Python. Приведенный выше код, в котором мы отфильтровали четные числа, может быть представлен в Python следующим образом:

filter(lambda x: x % 2 == 0, [1, 2, 3, 4])

В любом случае, лямбды не так эффективны без замыканий. Замыкания - вот что делает концепцию лямбда настолько сильной. В моем примере с мемоизацией я использовал замыкания, чтобы создать замыкание вокруг storeпараметра. Таким образом, у меня есть доступ к этому параметру даже после того, как memoizeфункция вернула свой результат (лямбда).

Ионуй Г. Стан
источник
3
Ух ты потратил на это много времени.
mk12
4
@ Mk12, в самом написании ответа не совсем. В изучении этого материала, да, я давно не начинал :)
Ionuț G. Stan
Хороший ответ, но отсутствует информация о «функциональных интерфейсах» (с точки зрения Java).
djangofan 02
Что это за операторы "===" в вашем "шаблоне абстрагирования"?
Дон
@Don см. Этот stackoverflow.com/questions/359494/…
Ionuț G. Stan
19

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

(1..100).select {|num| num % 2 == 0}

Это создаст массив, содержащий четные числа от 1 до 100. Нам не нужно писать явный цикл - метод select принимает функцию, которую он использует для проверки значений, поэтому все, что нам нужно, это наша настраиваемая логика. Это позволяет нам значительно настроить метод практически без усилий и накладных расходов. По сути, мы можем составлять функции из более мелких функций.

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

Чак
источник
6
Может быть, вам стоит добавить, что в трубках есть параметр. Я один из тех, кто плохо читает рубин.
Skurmedel 06
Это хороший ответ. Единственная причина, по которой Ионут получил мой голос, заключается в том, что он рассказал нам, почему мы должны заботиться (подробно) о лямбдах.
Frank Shearar
Я бы не согласился, что лямбды обычно закрываются.
jwg 08
6

«Лямбда» может быть слишком мало. Взгляните на лямбда-исчисление . Это полезно в функциональном программировании.

А функциональное программирование - это еще одна парадигма программирования (например, процедурная или объектно-ориентированная).

ЗДРАВСТВУЙ
источник
5
Затем люди говорят о «лямбде», скорее всего, они имеют в виду анонимную функцию, указатели функций, замыкание или что-то подобное. Практически никогда не упоминайте о реальном исчислении лямбда.
J-16 SDiZ 06
Рад, что вы упомянули лямбда-исчисление! +1.
RBT
5

Лямбды в .NET довольно часто называют «синтаксическим сахаром». Они не влияют напрямую на функциональность, но упрощают использование языка людьми.

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

Красный квадрат
источник
1
Я не думаю, что кто-то упоминал .NET, поэтому OP, вероятно, лучше с более общим ответом.
molf 06
2
вот почему я пояснил, отвечая на вопрос о .net. Если другие будут щебетать с их реализацией языка, тогда вопросы и ответы помогут многим людям, независимо от того, какой язык они выберут.
redsquare 06
Эти ответы выглядят так, как будто вы что-то слышали о лямбдах, но сами еще не понимаете их.
jwg 08
2

В Dr Dobbs Journal есть полезная статья о лямбда-выражениях. (в контексте C ++, но я думаю, что вы можете применить эти принципы к любому языку).

Как говорится в статье: «Лямбда-выражение - это очень компактное выражение, которое не требует отдельного определения класса / функции».

Поэтому, используя примеры листингов 1 и 2 из DDJ вместо записи:

std::for_each( vec.begin(), vec.end(), print_to_stream<std::string>(std::cout));

Для этого требуется отдельное определение класса, например:

template <typename T, typename Stream> class print_to_stream_t {
  Stream& stream_;
public:
  print_to_stream_t(Stream& s):stream_(s) {}
  void operator()(const T& t) const {
    stream_ << t;
  }
};
template <typename T,typename Stream> 
print_to_stream_t<T,Stream>   print_to_stream(Stream& s) {
  return print_to_stream_t<T,Stream>(s);
}

Используя лямбда-библиотеку Boost, это может стать:

std::for_each(vec.begin(),vec.end(),std::cout << _1);

Что сохраняет определение встроенным.

В статье также объясняются некоторые другие применения лямбда-выражений.

Я думаю, что ключевой момент в статье DDJ : «Обычно лямбда-выражения используются, когда на месте вызова требуются небольшие и не слишком сложные функции. Если бы функция была нетривиальной, вам бы не понадобилось лямбда-выражение, а обычная функция или функциональный объект ".

Данио
источник
2

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

TR
источник
2

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

Математики и логики склонны использовать странные слова.

«лямбда» звучит действительно эзотерически - как будто это очень странная и особенная вещь. На самом деле, если вы пишете JavaScript для приложения веб-браузера и используете идиому «var foo = function () {...}», вы всегда использовали лямбда-функции.

Питер Мортенсен
источник
1

Лямбда-выражение - это простая форма функции. Идея состоит в том, что что-то от формы слева (эквивалентное параметрам) становится чем-то вроде формы справа (эквивалентным телу).

например, до-диез:

x => x * x

лямбда для вычисления квадрата значения. Что-то в форме

x

становится чем-то вроде

x * x
Дэйв Кузино
источник
0

«Лямбда-выражение - это анонимная функция, которая может содержать выражения и инструкции, и может использоваться для создания делегатов или типов дерева выражений.

Во всех лямбда-выражениях используется лямбда-оператор =>, который читается как «переходит к». Левая часть лямбда-оператора определяет входные параметры (если есть), а правая часть содержит блок выражения или оператора. Лямбда-выражение x => x * x читается как «x переходит в x, умноженное на x».

из MSDN

Фермин
источник
4
Да, Microsoft заставляет всех думать, что они это изобрели. Однако лямбда-выражения появились раньше Microsoft. Это математический термин, который применялся к нескольким языкам программирования. (Что возможно, поскольку математика сама по себе может считаться компьютерным языком.)
Вим тен Бринк
4
Обратите внимание, что это характерно для реализации Microsoft .Net. Это не большое отклонение от общей идеи лямбда, но я думаю, что функциональность, реализованная в Лиспе, является более «стандартной».
Чак
2
Этот ответ вообще не объясняет, что такое лямбда (требуются определения анонимной функции, делегата, типа дерева выражения) и, конечно, не объясняет, каково его значение.
danio 06
0

Полное объяснение лямбда-выражений можно найти в Википедии . (Прокрутите вниз до раздела « Лямбда-исчисление и языки программирования» .) Лямбда-выражения не новы, и они не просто часть C #, а то, что было введено в вычисления почти 80 лет назад! Лямбда-выражения являются основой функционального программирования.

Это ценность? Что ж, учитывая, что он на самом деле довольно старый, я бы сказал: очень ценный для любого, кто делает вычисления.

Вим тен Бринк
источник
0

Если вы занимаетесь Java, вы много слышали о лямбдах или замыканиях за последние пару месяцев, потому что были разные предложения по добавлению этой функции в Java 7. однако, я думаю, что участник отказался от нее. Одно из предложений сделано Нилом Гафтером и подробно описано здесь: javac.info . Это помогло мне понять варианты использования и преимущества (особенно по сравнению с внутренними классами).

Тим Бют
источник
-1

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

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

Лямбда-выражения делают целесообразным использование обратных вызовов даже для самых крошечных задач, что может сделать код более понятным. Может не.

Има
источник