2012-10-30 54 views
5

我在currying函數中刪除Haskell中的三個參數時遇到了麻煩。柯西3在Haskell中的參數

免責聲明:不是課程作業,我被一個今天掙扎着的人問了這個問題,它一直在困擾着我。

我們給出的自定義類型/職能是(只能記住類型)

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的天才可以指出爲什麼我是一個傻瓜...

+0

你可以只使用['pointfree'](http://www.haskell.org/ haskellwiki/Pointfree#Tool_support)自動執行此操作。 – kennytm

+1

大喊,會調查一下! –

回答

10

所有你所需要的是運營商部分的以下三個「法律」:

(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 

您應該只爲你滿意做盡可能多pointfree改造,使產生的表達式仍然可讀 - 而事實上,更清晰比原來的。 「無點」工具有時會產生無法讀取的結果。

停在中間完全可以。如果你手動完成它太難了,可能也很難閱讀它。

((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)) 
+1

很好的答案,謝謝! –

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

我認爲你做所有的辛勤工作,並有一隻離開寸步。

你確實可以用pointfree自動做到這一點。見HaskellWiki page

因爲它在github readme說,一旦你安裝了它,你可以用線

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

編輯ghci.conf.ghci文件,然後在ghci中,當你鍵入

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

甚至

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

funcB = ((!!) .) . iterate . funcA 
+3

這不是eta的減少。埃塔正在從'\ x - > e x'轉向'e'。你所提供的轉換(從'\ x - > f(g x)'移動到'f。g')是另一回事 - 也許是'(。)'的​​增量擴展? –

+0

如果你仔細閱讀一下,你會發現我沒有做出你所說的轉變! – AndrewC

+0

是的,我後來意識到三角洲擴張只是您所做的轉型的一部分,但它已經通過了五分鐘不收回的標誌! –

4

的關鍵發現是,中綴運算符可以寫成前綴:

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

一旦你在這裏得到了,你必須認識到這是一半的機會的組合物,與(.) (!!)作爲第一個參數和iterate . funcA作爲第二個參數:

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)