Развлечения с Lambdas

Не имея их использовать все это, я не совсем уверен, что все эти lambdas/blocks могут использоваться для (кроме синтаксиса локальных функций map/collect/do/lightweight). Если бы некоторые люди могли опубликовать некоторые интересные, но несколько понятные примеры (с объяснением).

предпочтительные языки для примеров: python, smalltalk, haskell

Ответ 1

Вы можете создать функциональную структуру данных из lambdas. Вот простой - функциональный список (Python), поддерживающий методы add и contains:

empty = lambda x : None

def add(lst, item) :
    return lambda x : x == item or lst(x)

def contains(lst, item) :
    return lst(item) or False

Я просто быстро закодировал это для удовольствия - обратите внимание, что вам не разрешено добавлять какие-либо значения фальши. Он также не является хвостовым рекурсивным, поскольку должна быть хорошая функциональная структура. Упражнения для читателя!

Ответ 2

Вы можете использовать их для потока управления. Например, в Smalltalk метод ifTrue: ifFalse: является методом для логических объектов с другой реализацией в каждом из классов True и False. Выражение

someBoolean ifTrue: [self doSomething] ifFalse: [self doSomethingElse]

использует два замыкания --- блоки в квадратных скобках в синтаксисе Smalltalk --- один для истинной ветки и один для ложной ветки. Реализация "ifTrue: ifFalse:" для экземпляров класса True -

ifTrue: block1 ifFalse: block2
    ^ block1 value

и для класса False:

ifTrue: block1 ifFalse: block2
    ^ block2 value

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

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

(define (assq/default key lst default-thunk)
  (cond
    ((null? lst) (default-thunk)) ;; actually invoke the default-value-producer
    ((eq? (caar lst) key) (car lst))
    (else (assq/default key (cdr lst) default-thunk))))

Он будет называться вот так:

(assq/default 'mykey my-alist (lambda () (+ 3 4 5)))

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

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

ETA: где я сказал, что закрытие выше, я имею в виду лексически закрытые закрытия. Это лексический охват, который часто используется.

Ответ 3

Вы можете использовать lambda для создания Y Combinator, то есть функции, которая принимает другую функцию и возвращает рекурсивную форму. Вот пример:

def Y(le):
    def _anon(cc):
        return le(lambda x: cc(cc)(x))
    return _anon(_anon)

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

Ответ 4

Его С#, но я лично получаю удовольствие от этой статьи каждый раз, когда я ее читаю:

Создание данных из тонкого воздуха - реализация функций Lisp cons, car и cdr в С#. Он показывает, как полностью построить структуру данных стека из лямбда-функций.

Ответ 5

На самом деле это не совсем та же концепция, что и в haskell и т.д., но в С# конструкция lambda имеет (необязательно) возможность компиляции модели объекта objcet, представляющей код (деревья выражений), а не самого кода (это сам является одним из краеугольных камней LINQ).

Это, в свою очередь, может привести к некоторым очень выразительным возможностям метапрограммирования, например (где лямбда здесь выражается "с учетом службы, что вы хотите с ней делать?" ):

var client = new Client<ISomeService>();
string captured = "to show a closure";
var result = client.Invoke(
    svc => svc.SomeMethodDefinedOnTheService(123, captured)
);

(предполагая подходящую подпись Invoke)

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

Ответ 6

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

deriv f = \x -> (f (x + d) - f x) / d
  where
    d = 0.00001

f x = x ^ 2
f' = deriv f -- roughly equal to f' x = 2 * x