12

GHCi でいくつかの Haskell 関数をいじっています。

本当におかしな動作をしていて、なぜそうなっているのか不思議に思っています。

minこの関数は 2 つの値でのみ使用することになっていることに気付きました。ただし、3つの値を使用すると、私の場合

min 1 2 -5

私は得ています

-4

私の結果として。

何故ですか?

4

3 に答える 3

32

この式により、その結果が得られます。

min 1 2 -5

次のように括弧で囲まれたかのように解析します。

(min 1 2) -5

これはこれと同じです:

1 -5

これはこれと同じです:

1 - 5

これはもちろん -4 です。

Haskell では、関数の適用は最も厳密にバインドされた操作ですが、貪欲ではありません。実際、 のような一見単​​純な式でも、min 1 2実際には 2 つの別個の関数呼び出しになります。関数minは最初に 1 つの値 1 で呼び出されます。その関数の戻り値は新しい無名関数であり、1 とその単一の引数の間の小さい方の値を返します。次に、その無名関数が引数 2 で呼び出され、もちろん 1 が返されます。したがって、完全に括弧で囲まれたコードのより正確なバージョンは次のようになります。

((min 1) 2) - 5

しかし、すべてをそこまで分解するつもりはありません。ほとんどの場合、複数の引数を持つ関数呼び出しのように見えるものが、実際には一連の複数の単一引数関数呼び出しに変わるという事実は、手動で実行できる実装の詳細です。関数に渡す引数が少なすぎると、残りの引数で呼び出すことができる関数が返されることを知っておくことは重要ですが、ほとんどの場合、そのような呼び出しが内部で行われているという事実を無視できます。最初に正しい数の引数を渡したとしても。

したがって、 3 つの値の最小値を見つけるには、次の2 つの呼び出しを連鎖させる必要がありますmin(上記のロジックごとに実際には 4 つの呼び出しですが、これも手を振っています)。

min (min 1 2) (-5)

が中置減算ではなく前置否定として解釈されるようにするには、括弧-5を使用する必要があります。-それらがないと、元のコードと同じ問題が発生しますが、今回は Haskell に関数から数値を減算するように要求し、型エラーが発生します。

より一般的には、リストに折り畳みを適用することで、Haskell に連鎖を行わせることができます。リストには好きなだけ数を含めることができます。

foldl1 min [1, 2, -5]

(リテラル リスト構文では、コンマと角かっこが を区切り、-5明らかに減算演算ではないことに注意してください。したがって、ここではかっこは必要ありません。)

call fun listとは、「 listの最初の 2 つの項目を取り、それらに対してfunを呼び出すことを意味します。次に、その呼び出しの結果とlistの次の項目を取り、それら 2 つの値に対してfunを呼び出します。次に、その呼び出しの結果と、リストの次の項目...」というように、 listがなくなるまで続け、その時点でfunへの最後の呼び出しの値が元の呼び出し元に返されます。foldl1

の特定のケースについてはmin、既に定義されている折り畳みバージョンがあります: minimum。したがって、上記を次のように書くこともできます。

minimum [1, 2, -5]

それは私のfoldl1ソリューションとまったく同じように動作します。特に、空のリストが渡された場合はどちらもエラーをスローしますが、単一要素のリストが渡された場合は、 を呼び出すことなくその要素を変更せずに返しminます。

の存在を思い出させてくれた JohnL に感謝しminimumます。

于 2013-02-07T00:34:01.410 に答える
7

と入力するmin 1 2 -5と、Haskell はそれを としてグループ化しませんmin 1 2 (-5)。代わりに として解釈します(min 1 2) - 5。つまり、否定ではなく減算を行います。との最小値は明らかに で1あり、そこから減算すると (完全に正しく) が得られます。215-4

通常、Haskell では、この種のことが予期せず発生しないように、負の数を括弧で囲む必要があります。

于 2013-02-07T00:37:22.033 に答える