8

Haskellで3つの引数を削除する関数をカリー化するのに問題があります。

免責事項:コースワークではなく、今日これに苦労している誰かからこの質問をされました、そしてそれは私を悩ませてきました。

私たちが与えられたカスタムタイプ/関数は(タイプのみを覚えることができます)

type MyThing
  = (Char, String)
type MyThings
  = [MyThing]

funcA :: MyThings -> String -> String
funcB :: MyThings -> String -> Int -> String

私たちは始めました:

funcB as str n = iterate (funcA as) str !! n

そしてそれを次のように減らしました:

funcB as str n = iterate (funcA as) str !! n
funcB as str = (!!) . (iterate (funcA as)) str
funcB as = (!!) . (iterate (funcA as))
funcB as = (!!) . (iterate . funcA) as

その後、立ち往生。最後の引数の使用を避ける方法がわかりません。私は以前どこかで同様の状況を見たことがあり、解決策があったことを知っています。

Haskellの天才が私がばかである理由を指摘できることを願っています...

4

3 に答える 3

14

ここで必要なのは、演算子セクションの次の3つの「法則」です。

(a `op` b) = (a `op`) b = (`op` b) a = op a b
          (1)          (2)          (3)

オペランドが演算子の近くの空きスロットに入るようにします。

これは、次の(.)ことを意味します(a . b) = (a .) b = (. b) a = (.) a b。それで、

f (g x) y !! n      
= (!!) (f (g x) y) n              by (3) 
= ((!!) . f (g x)) y n
= ((!!) . (f . g) x) y n
= ((!!) .) ((f . g) x) y n        by (1)
= (((!!) .) . (f . g)) x y n
= (((!!) .) . f . g) x y n

結果の式が引き続き読みやすく、実際には元の式よりも明確になるように、ポイントフリー変換は快適な範囲でのみ実行する必要があります。「ポイントフリー」ツールは、読み取り不能な結果を​​生成する場合があります。

途中で止めても大丈夫です。手動で完了するのが難しい場合は、おそらくあなたもそれを読むのは難しいでしょう。

((a .) . b) x y = (a .) (b x) y = (a . b x) y = a (b x y)すぐに認識できるようになる一般的なパターンです。したがって、上記の式は次のようにかなり簡単に読み戻すことができます。

(!!) ((f . g) x y) n = f (g x) y !! n

それが連想的であることを考えると(.)

(a . b . c) = ((a . b) . c) = (a . (b . c))
于 2012-10-30T20:25:32.117 に答える
11
funcB = ((!!) .) . iterate . funcA

あなたはすべての大変な仕事をしたと思います、そしてほんの小さな一歩が残っていました。

あなたは確かにpointfreeでこれを自動的に行うことができます。HaskellWikiページを参照してください

github readmeに記載されているように、インストールすると、ghci.confまたは.ghciファイルを次の行で編集できます。

:def pf \str -> return $ ":! pointfree \"" ++ str ++ "\""

次に、入力するときにghciで

:pf funcB as = (!!) . (iterate . funcA) as

あるいは

:pf funcB as str n = iterate (funcA as) str !! n

あなたが得る

funcB = ((!!) .) . iterate . funcA
于 2012-10-30T13:27:46.233 に答える
5

私にとって重要な観察は、中置演算子は接頭辞と書くことができるということです:

funcB as = (!!) . (iterate . funcA) as
funcB as = (.) (!!) ((iterate . funcA) as)

ここに到達すると、これが構成であり(.) (!!)、最初の引数とiterate . funcA2番目の引数として認識される可能性が半分になります。

funcB as = (  ((.) (!!)) . (iterate . funcA)  ) as

これを単純化する方法は明らかです。その後、それを書く方法について多くの美的選択があります。たとえば、それ(.)が結合法則であることに気付くかもしれません。そのため、いくつかの括弧を削除できます。同様に、オペレーターセクションを使用して、見苦しいものをマージすることもできます((.) (!!))

funcB = (  ((.) (!!)) . (iterate . funcA)  )
funcB = (.) (!!) . iterate . funcA -- uncontroversial parenthesis removal
funcB = ((!!) .) . iterate . funcA -- possibly controversial section rewrite

ちなみに、あなたの派生の始まりは正しくないと思います。あなたは正しい結論に達しましたが、間違った中間のステップを経ています。修正すると、次のようになります。

funcB as str n = iterate (funcA as) str !! n
funcB as str n = (!!) (iterate (funcA as) str) n
funcB as str = (!!) (iterate (funcA as) str)
funcB as = (!!) . iterate (funcA as)
于 2012-10-30T17:45:22.680 に答える