8

私は 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

4 に答える 4

12

少し間接的な答えを2つ挙げます。

まず、次のコードについて考えてみます。

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

乱雑な注釈と冗長性を見渡すと、コンパイラーがすべてをラムダ式に変換したことがわかるはずです。これは、おそらく手動で行う必要がないことを示していると見なすことができます。

逆に、ラムダが必要になる可能性がある状況を考えてみましょう。関数のリストを作成するためにfoldを使用する関数は次のとおりです。

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

これはラムダでいっぱいであるだけでなく、main関数に対して2つの引数を取り、さらにfoldrそれらすべてに適用されます。foldr、、のタイプを上記と比較してください(a -> b -> b) -> b -> [a] -> b。どうやらそれは3つの引数を取るようですが、上記では4つに適用しました!アキュムレータ関数が2つの引数を取ることは言うまでもありませんが、ここでは3つの引数ラムダがあります。もちろん、秘訣は、両方が単一の引数を取る関数を返すことです。ラムダをジャグリングするのではなく、その場でその引数を適用しているだけです。

これらすべてが、うまくいけば、2つの形式が同等であることをあなたに確信させました。誰が違いを見分けることができるので、ラムダフォームは決して必要ではないか、おそらく常に必要です。

于 2011-08-18T22:35:20.760 に答える
6

の間に意味的な違いはありません

f x y z w = ...

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

式スタイル (明示的なラムダ式) と宣言スタイルの主な違いは構文です。それが重要な状況の 1 つは、where句を使用する場合です。

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

名前付きローカル関数または部分適用に置き換えることで、明示的なラムダをどこでも使用せずに任意の Haskell プログラムを作成することが実際に可能です。

参照:宣言と式スタイル

于 2011-08-18T22:31:38.500 に答える
3

名前付きのカリー化された関数 ( Haskell など) を宣言できる場合、明示的なラムダ式を使用する必要deriv-approxはありません。すべての明示的なラムダ式は、ラムダ式の自由変数を最初のパラメーターとして受け取る名前付き関数の部分適用に置き換えることができます。

なぜソース コードでこれを行う必要があるのか​​を理解するのは簡単ではありませんが、一部の実装では基本的にそのように動作します。

また、少し余談ですが、次の書き換え (私が今説明したものとは異なります) は、ラムダを回避していると見なされますか?

deriv-approx f = let myfunc h x = (f(x+h)-(f x))/h in myfunc
于 2011-08-18T22:27:09.370 に答える
0

関数を1回だけ使用する場合、たとえば、マップやフォルダー、またはその他の高階関数のパラメーターとして使用する場合は、関数が使用されていないことがすぐに明らかになるため、名前付き関数よりもラムダを使用する方がよい場合がよくあります。他の場所-名前がないため、そうすることはできません。新しい名前付き関数を導入すると、コードを読んでいる人に、スコープの期間中覚えておくべき別のことを与えることができます。したがって、ラムダは厳密に言えば必要というわけではありませんが、多くの場合、代替よりも好ましいです。

于 2011-08-21T13:19:37.843 に答える