Что такое лямбда (функция)? - PullRequest
686 голосов
/ 19 августа 2008

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

Ответы [ 22 ]

1011 голосов
/ 19 августа 2008

Лямбда происходит от Лямбда-исчисления и относится к анонимным функциям в программировании.

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

Python

def adder(x):
    return lambda y: x + y
add5 = adder(5)
add5(1)
6

Как видно из фрагмента Python, функция adder принимает аргумент x и возвращает анонимную функцию или лямбду, которая принимает другой аргумент y. Эта анонимная функция позволяет вам создавать функции из функций. Это простой пример, но он должен передать силу лямбды и замыкания.

Примеры на других языках

Perl 5

sub adder {
    my ($x) = @_;
    return sub {
        my ($y) = @_;
        $x + $y
    }
}

my $add5 = adder(5);
print &$add5(1) == 6 ? "ok\n" : "not ok\n";

JavaScript

var adder = function (x) {
    return function (y) {
        return x + y;
    };
};
add5 = adder(5);
add5(1) == 6

JavaScript (ES6)

const adder = x => y => x + y;
add5 = adder(5);
add5(1) == 6

Схема

(define adder
    (lambda (x)
        (lambda (y)
           (+ x y))))
(define add5
    (adder 5))
(add5 1)
6

C # 3,5 или выше

Func<int, Func<int, int>> adder = 
    (int x) => (int y) => x + y; // `int` declarations optional
Func<int, int> add5 = adder(5);
var add6 = adder(6); // Using implicit typing
Debug.Assert(add5(1) == 6);
Debug.Assert(add6(-1) == 5);

// Closure example
int yEnclosed = 1;
Func<int, int> addWithClosure = 
    (x) => x + yEnclosed;
Debug.Assert(addWithClosure(2) == 3);

Swift

func adder(x: Int) -> (Int) -> Int{
   return { y in x + y }
}
let add5 = adder(5)
add5(1)
6

PHP

$a = 1;
$b = 2;

$lambda = function () use (&$a, &$b) {
    echo $a + $b;
};

echo $lambda();

Haskell

(\x y -> x + y) 

Java см. этот пост

// The following is an example of Predicate : 
// a functional interface that takes an argument 
// and returns a boolean primitive type.

Predicate<Integer> pred = x -> x % 2 == 0; // Tests if the parameter is even.
boolean result = pred.test(4); // true

Lua

adder = function(x)
    return function(y)
        return x + y
    end
end
add5 = adder(5)
add5(1) == 6        -- true

Котлин

val pred = { x: Int -> x % 2 == 0 }
val result = pred(4) // true

рубин

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

def adder(x)
  lambda { |y| x + y }
end
add5 = adder(5)
add5[1] == 6

Ruby - это Ruby, для ламбд есть сокращение, поэтому вы можете определить adder следующим образом:

def adder(x)
  -> y { x + y }
end
101 голосов
/ 19 августа 2008

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

Например, вот фрагмент кода на C #, который не использует лямбда-выражения:

public Int32 Add(Int32 a, Int32 b)
{
    return a + b;
}

public Int32 Sub(Int32 a, Int32 b)
{
    return a - b;
}

public delegate Int32 Op(Int32 a, Int32 b);

public void Calculator(Int32 a, Int32 b, Op op)
{
    Console.WriteLine("Calculator: op(" + a + ", " + b + ") = " + op(a, b));
}

public void Test()
{
    Calculator(10, 23, Add);
    Calculator(10, 23, Sub);
}

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

В C # 2.0 мы получили анонимные методы, которые сокращают приведенный выше код до:

public delegate Int32 Op(Int32 a, Int32 b);

public void Calculator(Int32 a, Int32 b, Op op)
{
    Console.WriteLine("Calculator: op(" + a + ", " + b + ") = " + op(a, b));
}

public void Test()
{
    Calculator(10, 23, delegate(Int32 a, Int32 b)
    {
        return a + b;
    });
    Calculator(10, 23, delegate(Int32 a, Int32 b)
    {
        return a - b;
    });
}

А затем в C # 3.0 мы получили лямбда-выражения, которые делают код еще короче:

public delegate Int32 Op(Int32 a, Int32 b);

public void Calculator(Int32 a, Int32 b, Op op)
{
    Console.WriteLine("Calculator: op(" + a + ", " + b + ") = " + op(a, b));
}

public void Test()
{
    Calculator(10, 23, (a, b) => a + b);
    Calculator(10, 23, (a, b) => a - b);
}
60 голосов
/ 19 августа 2008

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

Лисп использовал лямбда-концепцию для именования литералов своих анонимных функций. Эта лямбда представляет функцию, которая принимает два аргумента, x и y, и возвращает их произведение:

(lambda (x y) (* x y)) 

Это может быть применено в строке, как это (оценивается в 50 ):

((lambda (x y) (* x y)) 5 10)
56 голосов
/ 29 августа 2008

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

Простой пример (с использованием Scala для следующей строки):

args.foreach(arg => println(arg))

где аргумент метода foreach является выражением для анонимной функции. Вышеприведенная строка более или менее совпадает с написанием чего-то подобного (не совсем реального кода, но вы поймете):

void printThat(Object that) {
  println(that)
}
...
args.foreach(printThat)

за исключением того, что вам не нужно беспокоиться:

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

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

int tempVar = 2 * a + b
...
println(tempVar)

вместо того, чтобы просто написать выражение там, где оно вам нужно:

println(2 * a + b)

Точная запись варьируется от языка к языку; Греческий не всегда требуется! ; -)

45 голосов
/ 14 августа 2013

Лямбда-исчисление является последовательной математической теорией замещения. В школьной математике можно увидеть, например, x+y=5 в паре с x−y=1. Наряду со способами манипулирования отдельными уравнениями также возможно объединить информацию из этих двух, при условии, что подстановки между уравнениями выполняются логически. Лямбда-исчисление кодифицирует правильный способ сделать эти замены.

Учитывая, что y = x−1 является действительной перестановкой второго уравнения, это: λ y = x−1 означает функцию, заменяющую символы x−1 на символ y. Теперь представьте себе применение λ y к каждому члену в первом уравнении. Если срок равен y, тогда выполните замену; иначе ничего не делать. Если вы сделаете это на бумаге, вы увидите, как применение этого λ y сделает первое уравнение разрешимым.

Это ответ без какой-либо информатики или программирования.

Простейший пример программирования, который я могу себе представить, взят из http://en.wikipedia.org/wiki/Joy_(programming_language)#How_it_works:

вот как квадратная функция может быть определена в императиве язык программирования (C):

int square(int x)
{
    return x * x;
}

Переменная x является формальным параметром, который заменяется фактическим значение в квадрате при вызове функции. В функционале На языке (Схеме) будет определена та же функция:

(define square
  (lambda (x) 
    (* x x)))

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


Добавлено: http://imgur.com/a/XBHub

lambda

14 голосов
/ 19 августа 2008

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

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

Например (в C # 3):

LinqToSqlContext.Where( 
    row => row.FieldName > 15 );

LinqToSql может прочитать эту функцию (x> 15) и преобразовать ее в фактический SQL для выполнения с использованием деревьев выражений.

Вышеприведенное утверждение становится:

select ... from [tablename] 
where [FieldName] > 15      --this line was 'read' from the lambda function

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

Не все методы в C #, которые используют лямбда-синтаксис, могут быть скомпилированы в деревья выражений (т.е. фактические лямбда-функции). Например:

LinqToSqlContext.Where( 
    row => SomeComplexCheck( row.FieldName ) );

Теперь дерево выражений не может быть прочитано - SomeComplexCheck не может быть разбит. Оператор SQL будет выполняться без где, и каждая строка данных будет указана через SomeComplexCheck.

Лямбда-функции не следует путать с анонимными методами. Например:

LinqToSqlContext.Where( 
    delegate ( DataRow row ) { 
        return row.FieldName > 15; 
    } );

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

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

7 голосов
/ 19 августа 2008

Пример лямбды в Ruby выглядит следующим образом:

hello = lambda do
    puts('Hello')
    puts('I am inside a proc')
end

hello.call

Будет генерировать следующий вывод:

Hello
I am inside a proc
7 голосов
/ 19 августа 2008

Мне нравится объяснение Lambdas в этой статье: Эволюция LINQ и ее влияние на дизайн C # . Это имело большой смысл для меня, поскольку оно показывает реальный мир для Lambdas и строит его как практический пример.

Их краткое объяснение: лямбда-выражения - это способ обработки кода (функций) как данных.

5 голосов
/ 19 августа 2008

@ Брайан Я все время использую лямбды в C #, в операторах LINQ и не LINQ. Пример:

string[] GetCustomerNames(IEnumerable<Customer> customers)
 { return customers.Select(c=>c.Name);
 }

До C # я использовал анонимные функции в JavaScript для обратных вызовов функций AJAX, еще до того, как термин Ajax был даже придуман:

getXmlFromServer(function(result) {/*success*/}, function(error){/*fail*/});

Интересная особенность лямбда-синтаксиса в C # заключается в том, что сам по себе их тип не может быть выведен (т. Е. Вы не можете ввести var foo = (x, y) => x * y), но в зависимости от того, какой тип они назначены, они будут скомпилированы как делегаты или абстрактные синтаксические деревья, представляющие выражение (именно так средства отображения объектов LINQ выполняют свою «интегрированную в язык» магию).

Лямбды в LISP также могут быть переданы оператору кавычек и затем переданы в виде списка списков. Некоторые мощные макросы сделаны таким образом.

5 голосов
/ 31 января 2017

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

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

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

...