Когда лямбда-формы необходимы в Haskell? - PullRequest
8 голосов
/ 19 августа 2011

Я новичок в Haskell и относительный новичок в функциональном программировании. В других (кроме Haskell) языках лямбда-формы часто очень полезны.

Например, на схеме:

(define (deriv-approx f)
  (lambda (h x)
    (/ (- (f (+ x h)
          (f x)
       h)))

Создает замыкание (по функции f) для аппроксимации производной (при значении x с интервалом h). Однако такое использование лямбда-формы, по-видимому, не является необходимым в Haskell из-за его частичного применения:

deriv-approx f h x = ( (f (x + h)) - (f x) ) / h

Какие примеры, когда лямбда-формы необходимы в Haskell?

Редактировать: заменить 'замыкание' на 'лямбда-форму'

Ответы [ 4 ]

12 голосов
/ 19 августа 2011

Я собираюсь дать два слегка косвенных ответа.

Во-первых, рассмотрим следующий код:

module Lambda where

derivApprox f h x = ( (f (x + h)) - (f x) ) / h

Я скомпилировал это, сказав GHC, чтобы вывести промежуточное представление,это примерно упрощенная версия Haskell, используемая как часть процесса компиляции, чтобы получить это:

Lambda.derivApprox
  :: forall a. GHC.Real.Fractional a => (a -> a) -> a -> a -> a
[LclIdX]
Lambda.derivApprox =
  \ (@ a) ($dFractional :: GHC.Real.Fractional a) ->
    let {
      $dNum :: GHC.Num.Num a
      [LclId]
      $dNum = GHC.Real.$p1Fractional @ a $dFractional } in
    \ (f :: a -> a) (h :: a) (x :: a) ->
      GHC.Real./
        @ a
        $dFractional
        (GHC.Num.- @ a $dNum (f (GHC.Num.+ @ a $dNum x h)) (f x))
        h

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

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

composeAll :: [a -> a] -> a -> a
composeAll = foldr (.) id

Что это?Не лямбда в поле зрения!Фактически, мы можем пойти и другим путем :

composeAll' :: [a -> a] -> a -> a
composeAll' xs x = foldr (\f g x -> f (g x)) id xs x

Мало того, что он полон лямбда-выражений, он также принимает два аргумента для главной функции и, более того,применяя foldr ко всем из них.Сравните тип foldr, (a -> b -> b) -> b -> [a] -> b с приведенным выше;очевидно, требуется три аргумента, но выше мы применили его к четырем!Не говоря уже о том, что функция аккумулятора принимает два аргумента, но здесь у нас есть лямбда с тремя аргументами.Хитрость, конечно, в том, что оба возвращают функцию, которая принимает один аргумент;и мы просто применяем этот аргумент на месте, вместо того, чтобы жонглировать лямбдами вокруг.

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

6 голосов
/ 19 августа 2011

Нет семантической разницы между

f x y z w = ...

и

f x y = \z w -> ...

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

f x y = \z w -> ...
   where ... -- x and y are in scope, z and w are not

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

См. Также: Декларация и стиль выражения .

3 голосов
/ 19 августа 2011

Когда вы можете объявить именованные функции карри (например, ваш Haskell deriv-approx), никогда не необходимо использовать явное лямбда-выражение.Каждое явное лямбда-выражение может быть заменено частичным применением именованной функции, которая принимает свободные переменные лямбда-выражения в качестве своих первых параметров.

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

Кроме того, несколько не в том смысле, будет ли следующее переписывание (отличное от того, что я только что описал) считать для вас исключением лямбды?

deriv-approx f = let myfunc h x = (f(x+h)-(f x))/h in myfunc
0 голосов
/ 21 августа 2011

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

...