Модераторы: Се ля ви

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> лямбда функции, не знаю что такое! 
V
    Опции темы
Ch0bits
Дата 14.5.2006, 21:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Python Dev.
****


Профиль
Группа: Завсегдатай
Сообщений: 2124
Регистрация: 21.2.2005
Где: Казань

Репутация: нет
Всего: 62



САБЖ
Объясните пожалуйста!
 smile  
PM WWW   Вверх
Void
Дата 14.5.2006, 22:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 9
Всего: 173



В контексте языков программирования под лямбда-функцией понимают анонимную функцию. Т.е. функцию, которой не сопоставлено никакое имя, просто значение функционального типа. Например:
C# 2.0:
Код
delegate(int x) { return x + 1; }

Код
lambda x: x + 1

OCaml:
Код
fun x -> x + 1

Haskell:
Код
\x -> x + 1

Lisp:
Код
(lambda (x) (+ x 1))

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


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
Ch0bits
Дата 15.5.2006, 11:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Python Dev.
****


Профиль
Группа: Завсегдатай
Сообщений: 2124
Регистрация: 21.2.2005
Где: Казань

Репутация: нет
Всего: 62



Ну ты полиглот smile  

Но я ничего не понял! smile 
Ни про исчисления, ни про замыкания...  smile  

Цитата(Void @  14.5.2006,  22:01 Найти цитируемый пост)
ортогональные понятия

Как это понимать? 
PM WWW   Вверх
Void
Дата 15.5.2006, 19:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 9
Всего: 173



Замыкание, это, фактически, захват контекста вызова. Т.е. в точке вызова функции доступны переменные, которые были в зоне видимости определения функции, но не видны в самой точке вызова. Например:
Код
delegate void Lambda(int x);

static void foo(Lambda f)
{
    f(3);
}

static void bar()
{
    int n = 4;
    foo(delegate(int x) { n = x; });
    Console.WriteLine(n);
}

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

А про лямбда-исчисление написано в замечательной книги Харрисона и Филда «Функциональное программирование», которая есть на lib.ru.

Ортогональные — значит не зависят друг от друга, одно не является следствием или непременным атрибутом другого. 


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
Ch0bits
Дата 15.5.2006, 20:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Python Dev.
****


Профиль
Группа: Завсегдатай
Сообщений: 2124
Регистрация: 21.2.2005
Где: Казань

Репутация: нет
Всего: 62



В чём же преимущества такого подхода? Я пока вижу одну путаницу.  smile  
PM WWW   Вверх
Void
Дата 15.5.2006, 21:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 9
Всего: 173



Преимущества появляются, когда есть адекватный синтаксис и нормальная поддержка функций высшего порядка [1] со стороны библиотеки. В C# 2.0 первое хромает, а второе находится в зачаточном состоянии.
Преимущества в том, что многие операции можно параметризовать функциями. И очень часто функции, которые надо передать, имеют небольшой размер и имеют значение только для маленького участка кода, где они применяются. Отсюда возникает необходимость в синтаксисе, позволяющем описывать такие функции прямо по месту использования, не засоряя пространство имен. Далее, логично было бы использовать в этой анонимной функции переменные, описанные в текущей функции и внешнем пространстве имен.

[1] — функцией высшего порядка называется функция, принимающая другие функции в качестве параметров и/или возвращающая функциональные значения.

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

Шаг первый: ни анонимных функций, ни замыканий. Чистый Си.
Код
typedef /* какая структра */ IntList;

int find(IntList *list, int (*predicate)(int)) { /* реализация */ }

void test()
{
    
    IntList *t; /* наш список */
    int n; /* какое-то значение */
    /* что-то мы с ними делали и возжелали найти в
       t первый элемент, больший n */
    x = find(t, /* Обломись! А что здесь писать-то? */);
    ...
}

Мы можем написать функцию greater где-то в файле, но передать значение n в нее мы можем только через глобальную переменную, а делать этого нам бы не хотелось. Выход один: добавить в find еще один параметр, который она будет передавать предикату при каждом его вызове. Поскольку предикату может понадобиться все что угодно, параметр придется делать типа void*. Прощай, типобезопасность, но мы же все-таки на Си пишем. Итак, новый вариант:
Код
int *find(IntList *list, void *param, int (*predicate)(int, *void));

int greater(int x, void *param) {
    return x >= *((int*) param);
}

...
int *x = find(t, &n, greater);

Не очень красиво, но достаточно гибко.

Шаг один с половиной. Анонимных функций нет, замыкания есть, но толку от них… Паскаль.
Код
type IntList = { какая структура };

type Predicate = function(x: Integer): Boolean;

function Find(list: IntList; f: Predicate): Integer;
begin
    { реализация }
end;

procedure Test;
    var t: IntList;
    var n: Integer;

    function GreaterN(x: Integer): Boolean;
    begin
        GreaterN := x >= n
    end;
begin
    ...
    x := Find(t, GreaterN); { Опа, что это у нас тут? }
end;

Ошибка компиляции. Объявленные таким образом функции нельзя передавать в другие функции. Внутри области видимости используйте сколько угодно, они будут иметь доступ ко внешним переменным. А передать — ни-ни. Ну и зачем они такие нужны? А решение проблемы в итоге такое же, как в Си.

Шаг два. В языке вроде бы нет ни того, ни другого, но все можно сделать своими руками. C++.
Код
/* Не буду объвлять структуру List и функцию find!
   Да здравствует ООП и стандартная библиотека. */
#include <list>
#include <algorithm>

void test() {
    // здесь все знакомо:
    list<int> t;
    int n;
    // и не надоело число в списке искать?
    
    // Нам нужен предикат.
    class Greater {
    private:
        int &n; // ссылка на перменную окружения, которая нам нужна
    public:
        // кто-то добрый все-таки должен передать предикату n —
        // но всего один раз!
        Greater(int &n) : n(n) { }
        
        bool operator()(int x) const { return x > n; }
    };
    list<int>::iterator x = find_if(
        t.begin(), t.end(), // ищем от начала и до конца
        Greater(n) // вот оно, замыкание ручками
    );
}

Синтаксис не блещет, но задачу мы все-таки решили, не вылезая за пределы своей функции и не теряя типобезопасности (хвала шаблонам). А предикат в виде класса можно вообще куда-нибудь в библиотеку выделить. Что, впрочем, создатели стандартной библиотеки и сделали.
Код
// делаем то же самое средствами STL
find_if(t.begin(), t.end(), bind2nd(greater<int>(), n));

Запись стала сущестенно короче, но не слишком понятнее.
Отдельные индивидуумы пошли дальше, и задействовав головокружительную смесь шаблонов и перегрузки операторов, сумели создать библиотеку, позволяющую писать так:
Код
find_if(t.begin(), t.end(), _1 > n);

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

Шаг третий. Есть и анонимные функции и замыкания. Вот только пользоваться ими не очень удобно. C# 2.0.
Код
// стандартная библиотека по-прежнему рулит
List<int> t = new List<int>();
int n;
...
int x = t.Find(delegate(int x) { return x > n; });

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

Шаг три с половиной. Синтаксис рулит, библиотеку писали с учетом всех возможностей языка. OCaml.
Код
let t = [1; 2; 3; 4]
and n = 3 in
let x = List.find (fun x -> x > n) t

Или даже так:
Код
List.find ((<=) n) t

Почему всего полшага? Если бы не один просчет создателей .NET Framework, я бы поставил OCaml на одну полку с C#. Дело в том, что функции высшего порядка в стандартной библиотеке что там, что там неполиморфны. В .NET нет функций, работающих с IList или IEnumerable. Только методы List, Array и т.д., хотя сам язык позволяет их написать. OCaml увы, этого рода полиморфизма лишен из-за свойств самого языка.

Шаг четвертый. То же самое, плюс полиморфизм. Haskell (за счет type classes). Одна и та же функция будет работать и со списком, и с массивом, и с черт знает чем, лишь бы те предоставляли некоторый интерфейс. Код приводить не буду, т.к. знаком с языком очень поверхностно, лишь осведомлен о возможности такой реализации.

Disclaimer: я упомянул далеко не все языки, поддерживающие замыкания и анонимные функции. Например, Python и Ruby (особенно Ruby) замечательно поддерживают и то и другое. И даже полиморфизм работает, правда, за счет duck typing'а со всеми вытекающими.

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

Это сообщение отредактировал(а) Void - 15.5.2006, 22:09


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
batigoal
Дата 16.5.2006, 08:42 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Нелетучий Мыш
****


Профиль
Группа: Участник Клуба
Сообщений: 6423
Регистрация: 28.12.2004
Где: Санктъ-Петербургъ

Репутация: 1
Всего: 151



Void, я потрясён. 


--------------------
"Чтобы правильно задать вопрос, нужно знать большую часть ответа" (Р. Шекли)
ЖоржЖЖ
PM WWW   Вверх
Void
Дата 16.5.2006, 16:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 9
Всего: 173



Lamer George, спасибо smile

to all: Буду очень благодарен за любые дельные замечания к посту. Думаю, после некоторой обработки это можно разместить в Вики. 


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
ALKS
Дата 16.5.2006, 17:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 354
Регистрация: 22.3.2006

Репутация: нет
Всего: 11



Хм.... а пример того же на Java?  smile 
PM   Вверх
Void
Дата 16.5.2006, 17:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 9
Всего: 173



ALKS, увы, Java я в лучшем случае могу читать. AFAIK, замыкания в Java можно изобразить на анонимных классах.

Добавлено @ 17:23 
Уточнил в Гугле. Inner classes в Java копируют контекст вызова, а не захватывают его. Т.е. мы можем использовать внешние переменные, но не модифицировать их. 


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
maxim1000
Дата 16.5.2006, 18:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 1
Всего: 110



Цитата(Void @  15.5.2006,  20:51 Найти цитируемый пост)
Шаг два. В языке вроде бы нет ни того, ни другого, но все можно сделать своими руками. C++.

как-то пробовал так делать... ругается smile
не хочет использовать локальный тип при специализации шаблона
компилятор из VC++ 2003 Toolkit
вопрос: это самодеятельность Microsoft или так должно быть по стандарту? 


--------------------
qqq
PM WWW   Вверх
maxim1000
Дата 16.5.2006, 18:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 1
Всего: 110



зато можно так (только в том случае если тот не работает):
Код

class CFunc
{
  public:
    virtual void function()=0;
};
void MakeSomething(CFunc &f)
{
  f.function();
}

void f()
{
  class myfunc:public CFunc
  {
    public:
      virtual void function()
      {
        cout<<"qqq";
      }
  };
  MakeSomething(myfunc());
}

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


--------------------
qqq
PM WWW   Вверх
svg
Дата 16.5.2006, 20:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 53
Регистрация: 13.4.2005

Репутация: нет
Всего: 18



Цитата(Void @  16.5.2006,  16:51 Найти цитируемый пост)
to all: Буду очень благодарен за любые дельные замечания к посту. Думаю, после некоторой обработки это можно разместить в Вики.  


Полиморфная find (erlang)
Код

find([], _) ->
  nil;
find([X|Rest], Test) ->
  case Test(X) of
    true ->
      X;
    false ->
      find(Rest, Test)
  end;
find(T, Test) when is_tuple(T) ->
  find(tuple_to_list(T), Test).

Example:

find([1, 2, 3], fun (X=3) -> true; (_) -> false end).
   

Это сообщение отредактировал(а) svg - 16.5.2006, 20:15
PM MAIL   Вверх
Ch0bits
Дата 16.5.2006, 21:49 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Python Dev.
****


Профиль
Группа: Завсегдатай
Сообщений: 2124
Регистрация: 21.2.2005
Где: Казань

Репутация: нет
Всего: 62



svg
Это что у тебя за язык?

Void
Читал 5 раз, в разное время суток. Вроде что-то понял, но мне до этого расти да расти.   smile 
Лови плюс. smile  
PM WWW   Вверх
Void
Дата 16.5.2006, 22:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 9
Всего: 173



Ch0bits, это главное — понимать, что есть куда расти, и стремиться к этому smile

Цитата(Ch0bits @  16.5.2006,  23:49 Найти цитируемый пост)
Это что у тебя за язык?

Erlang 


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила раздела "Философия программирования":
Се ля ви

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

• вопросы перспективного развития методов написания ПО;

• изменяющиеся языки и методологии программирования;


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Се ля ви.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Философия программирования | Следующая тема »


 




[ Время генерации скрипта: 0.1095 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.