Кажется, я просто не могу разобраться с Lambdas в c # - PullRequest
3 голосов
/ 15 сентября 2010

Мне не удалось понять, как создавать и использовать лямбда-выражения. Я знаю, как использовать их в операторах linq, но я действительно не понимаю, что происходит за кулисами. Я также не смог найти полное руководство о том, когда их использовать, как их определить и т. Д.

Вторая часть ...

Говорят, что Javascript - это язык LAMBDA, я достаточно хорошо знаю javascript, просто интересно, какие типы концепций применимы к jamascript lambdas и c # lambdas.

третья часть ...

В чем разница между функциональным языком и лямбда-языком?

Есть предложения?

Ответы [ 9 ]

8 голосов
/ 15 сентября 2010

Позвольте мне рассказать вам, что происходит за кулисами.Это проще, чем вы думаете.

Предположим, у вас есть:

delegate int D(int x);
...
class C
{
    void M(int y)
    {
        int z = 123;
        D d = x=>x+y+z;
        Console.WriteLine(d(10));
        z = 345;
        y = 789;
        Console.WriteLine(d(10));
   }
}

Все, что делает компилятор, делает вид, что вы написали:

delegate int D(int x);
...
class C
{  
    private class Locals
    {
        public int y;
        public int z;
        public int A(int x)
        {
            return x + this.y + this.z;
        }
    }
    void M(int y)
    {
        // Initialize the closure class:

        Locals locals = new Locals();
        locals.y = y;

        // Transform the body so that all usages of y, z and the lambda
        // refer to the closure class:

        locals.z = 123;
        D d = locals.A;
        Console.WriteLine(d(10)); // Calls locals.A(10)
        locals.z = 345;
        locals.y = 789;
        Console.WriteLine(d(10)); // Calls locals.A(10)

    }
}

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

Закрытия функций в JScript работают по сути одинаково.JScript, конечно, не является языком, основанным на классах, поэтому детали немного отличаются, но идея та же.В C # вновь созданный объект делегата отслеживает класс locals, который имеет переменную состояние.В JScript вновь созданный объект функции имеет ссылку на фрейм активации функции, которая создала замыкание, которая в основном представляет собой ту же информацию.

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

5 голосов
/ 15 сентября 2010

Я не могу ответить на третью часть, но позвольте мне взяться за 1 и 2, и, возможно, это поможет вам с 3.

Вы понимаете делегатов? Потому что это действительно все, с чем ты имеешь дело. Документация VS2010 очень лаконична.

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

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

Что касается чтения лямбды, мне не нравится терминология "идет в", используемая некоторыми. Я склонен читать это как обычный метод, то есть:

"учитывая некоторый список параметров, выполнить этот код"

Итак, row => row.State == "NY" средства Msgstr "Для заданной строки верните значение true, если штат строки Нью-Йорк".

Может быть, я упрощаю, но я простой парень.

4 голосов
/ 15 сентября 2010

Я могу обратиться к части JavaScript.Поскольку вы можете объявлять анонимные функции в JS (var fn = function(){/* stuff */};, вы также можете передавать эти функции в качестве параметров другим функциям. Фактически, вы уже использовали лямбда-выражения, если вам когда-либо приходилось выполнять собственную процедуру сортировки. Например:

// Standard sort:
x = [4,3,6,7,1,5,2];
x.sort();

// Custom sort:
y = [
    {'val':4,'name':'four'},
    {'val':3,'name':'three'},
    {'val':6,'name':'six'},
    {'val':7,'name':'seven'},
    {'val':1,'name':'one'},
    {'val':5,'name':'five'},
    {'val':2,'name':'two'},
];
y.sort(function(a,b){ return a.val > b.val ? 1 : -1 });

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

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

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

Во-первых, сам Widget. Обратите внимание, что Widget.prototype.publish() принимает один параметр, который является вашим пользовательскимформаттер:

var Widget = function() {
    var self = this;
    var strPrivateVar = "This is a private variable";
    self.publicVar = "This is a default public variable";
    self.publish = function(f) {
        var fnFormatter = f;
        var strOutput = "The output is " + fnFormatter(self,strPrivateVar);
        return strOutput;
    }
};

Далее ваши форматеры.Один дает краткое резюме, а другой - полный текст:

var fnSummary = function(o,s) {
    var self = o;
    var strPrivateVar = s;
    return strPrivateVar.substr(0,5) + ' ' + self.publicVar.substr(0,5);
}
var fnDetails = function(o,s) {
    var self = o; 
    var strPrivateVar = s;
    return strPrivateVar + ' ' + self.publicVar;
}

И, наконец, ваша реализация:

var wWidget = new Widget();
wWidget.publicVar = "I have overridden the public property";
var strSummary = wWidget.publish(fnSummary);
var strDetails = wWidget.publish(fnDetails);
console.log(strSummary,strDetails);

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

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

0 голосов
/ 15 сентября 2010

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

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

Думайте о лямбде как о функции, которая принимает параметр и возвращает значение.

0 голосов
/ 15 сентября 2010

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

Мне нравится думать о лямбдах в терминах функций / процедур, которые я определяю как локальные переменные, а не на уровне модуля.

Итак, допустим, у меня есть этот код:

public void Run()
{
    var template = "{0} inches is {1} centimetres.";
    Console.WriteLine(template, 2.0, 2.0 * 2.54);
    Console.WriteLine(template, 5.0, 5.0 * 2.54);
}

Очевидно, я не хочу повторять выражение * 2.54, поэтому я мог бы определить такую ​​функцию:

private double Inches2Centimetres(double inches)
{
    return inches * 2.54;
}

И мой код теперь становится:

public void Run()
{
    var template = "{0} inches is {1} centimetres.";
    Console.WriteLine(template, 2.0, Inches2Centimetres(2.0));
    Console.WriteLine(template, 5.0, Inches2Centimetres(5.0));
}

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

Я мог бы решить использовать лямбду, поэтому я могу написать это вместо:

public void Run()
{
    Func<double, double> inches2Centimetres = inches => inches * 2.54;

    var template = "{0} inches is {1} centimetres.";
    Console.WriteLine(template, 2.0, inches2Centimetres(2.0));
    Console.WriteLine(template, 5.0, inches2Centimetres(5.0));
}

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

Теперь, поскольку функция определена как переменная типа Func<...>, вы можете передавать лямбда-выражения как переменные.

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

public static T[] UsingDataReader<T>(
    this IDbConnection @this,
    string query,
    Func<IDataReader, T> transform)
{
    //...
}

В этом случае lamdba Func<IDataReader, T> transform может быть передано при вызове функции, и ему нужно только знать, как превратить текущую строку в объект типа T.Весь код для открытия соединения, выполнения команды чтения, перемещения между записями и полного закрытия всего аккуратно обрабатывается методом расширения.Мой код вызова гораздо более краткий.

string[] names = conn.UsingDataReader<string>("select FirstName from People;",
    dr => dr["FirstName"] as string);

Я знаю, что предоставил очень упрощенный ответ на ваш вопрос, но надеюсь, что это поможет.

0 голосов
/ 15 сентября 2010

Лямбда сама по себе не является «языком», поскольку она является частью языка C #. http://msdn.microsoft.com/en-us/library/bb397687(VS.90).aspx

Выражения Lambada являются «синтетическим сахаром», который устраняет много избыточного кодирования.

http://www.rvenables.com/tag/lambda-expressions/ имеет действительно хороший пример ярлыка, который предоставляет lambada.

http://www.codeproject.com/KB/cs/DelegatesOMy.aspx имеет другой пример до / после лямбды.

0 голосов
/ 15 сентября 2010

Лямбда-выражение - это анонимная функция первого класса. Когда вы объявляете один, вы создаете функцию, которая адресуется, но может иметь любое количество псевдонимов, как объект (следовательно, первоклассный). Вы можете передать его в переменных и параметрах. Javascript можно назвать лямбда-языком, потому что все функции javascript имеют эти свойства. Они могут быть созданы, добавлены в псевдонимы и переданы, и им не нужно иметь имя, подобное функциям в C do. Таким образом, они являются анонимными первоклассными функциями.

0 голосов
/ 15 сентября 2010

C # 3 в двух словах, а также книга Джона Скита хорошо освещают эту тему в своих книгах.

0 голосов
/ 15 сентября 2010

Это http://msdn.microsoft.com/en-us/magazine/cc163362.aspx может быть полезно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...