12

Haskell で関数をポイントフリー表記に変換する方法を理解しようとしています。この例を見ましたが、探しているものよりも複雑です。その背後にあるロジックを理解しているように感じますが、コードでいくつかの簡単な例を実行しようとすると、コンパイル エラーが発生します。この関数をポイントフリー スタイルで書きたいと思います。

f x = 5 + 8/x私は次のように再配置しましたf x = (+) 5 $ (/) 8 x

だから、私はそれが次のようなものかもしれないと思った:

f = (+) 5 $ (/) 8

しかし、これを ghci で実行すると、次のメッセージが表示されます。

No instance for (Num (a0 -> a0))
  arising from the literal `5' at Test.hs:3:9
Possible fix: add an instance declaration for (Num (a0 -> a0))
In the first argument of `(+)', namely `5'
In the first argument of `($)', namely `(+) 5'
In the expression: (+) 5 $ (/) 8
Failed, modules loaded: none.

「... のインスタンスがありません」というメッセージがわかりません。この関数をポイントフリー スタイルで記述するにはどうすればよいですか?

4

4 に答える 4

17

$の優先度は非常に低いです。つまり、f x = (+) 5 $ (/) 8 x実際には を意味しf x = (+) 5 $ ((/) 8 x)ます。代わりに、次のように書き換えます。

f x = (+) 5 ( (/) 8 x)
f x = ((+) 5) ( ((/) 8) x)
f x = ((+) 5) .  ( ((/) 8) ) x
f = ((+) 5) . ( (/) 8 )
f = (5+) . (8/)

最後の式は理にかなっています。f は 2 つの演算の合成です。最初に 8 を 1 の値で割り、次に結果に 5 を加算します。は「 hg.hを適用し、その結果を g に適用する」という意味です。

于 2011-12-11T16:39:20.787 に答える
12

ラムダ計算 (Haskell はその変形) の用語から SKI の用語 ( const( K )、id( I ) および<*>( S ) のみを使用する、完全に無意味な関数) への変換は、次の単純な規則で行うことができます。

  1. \x -> xに変換されidます。
  2. \x -> yx発生せずにy変換しconst yます。
  3. \x -> f gf' <*> g'どこに 翻訳する
    • f'\x -> fとの翻訳です
    • g'の翻訳です\x -> g

ここで、 がどこに入るのか疑問に思うかもしれません。最後の変換に.は特殊なケースがあります。fx\x -> f gconst f <*> (\x -> g)f . (\x -> g)

これらのルールを使用して、関数を変換できます。

f = \x -> ((+) 5) (((/) 8) x) = -- by the special-case (.) rule
((+) 5) . (\x -> (((/) 8) x)) = -- by eta-reduction ((\x -> f x) = f)
((+) 5) . ((/) 8)

イータリダクションは翻訳を完了するのに必要ではありませんが、イータリダクションがなければもっと厄介なことになります。たとえば、((+) 5) . ((/) 8) . id代わりに最後のステップが生成されます。

于 2011-12-11T23:01:17.763 に答える
11

「pointfree」プログラムは とともにインストールできcabal install pointfree、pointfree スタイルで式を記述する方法を示します。例えば:

$ pointfree "f x = 5 + 8/x"
f = (5 +) . (8 /)

この変換の説明:

  1. 中置/演算子関数には「セクション」を使用できます。(a +) == \b -> a + b(+ a) == \b -> b + a
  2. この.関数は、引数が 1 つの関数である 2 番目のパラメーターの結果を取得し、それを最初の引数に適用します。
于 2011-12-11T16:43:15.177 に答える
6

あなたは本当に近かった。$説明のためにもう 1 つ追加させてください。

f x = (+) 5 $ (/) 8 $ x

(+) 5式が 1 つの数値入力を取り、数値出力を生成する関数であることは明らかです。式も同様(/) 8です。したがって、入力された任意の数値を取りx、最初に(/) 8「関数」を適用し、次に(+) 5「関数」を適用します。

で区切られた一連の関数がある場合はいつでも$、右端を除くすべてを.意味に置き換えることができます。 がある場合a $ b $ c $ d、これは と同等a . b . c $ dです。

f x = (+) 5 . (/) 8 $ x

この時点で、代わりに and 括弧を実際に削除しましょう。$

f x = ((+) 5 . (/) 8) x

xこれで、両側から末尾を削除できることは明らかです。

f = (+) 5 . (/) 8

それが主な考え方です。を持っている場合はf x = expr x、それを に「イータ リデュース」できますf = expr。無意味なコードを作成するには、大きな関数が小さな関数で構成されていることを認識する必要があります。ポイントフリーコードの場合、部分的な適用が必要になる場合があります (この場合のように、(+) 5部分(/) 8的に適用されます)。「ポイントフリー」プログラムは、それについて考えたくない場合に非常に役立ちます。#haskell irc チャンネルの Lambdabot はこのプログラムをプラグインとして使用するため、自分でインストールする必要さえありません。ただ聞いてください:

<DanBurton> @pl let f x = 5 + 8 / x in f
<lambdabot> (5 +) . (8 /)
于 2011-12-11T22:56:55.447 に答える