2011-12-11 19 views
10

我想了解如何將函數轉換爲Haskell中的無點表示法。我看到this example,但它比我所尋找的更復雜。我覺得我理解它背後的邏輯,但是當我嘗試在代碼中執行一些簡單的示例時,我收到了編譯錯誤。我想嘗試寫在卡點式這樣的功能:簡單的哈斯克爾函數的免點風格

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. 

我不明白「沒有實例......」的消息。我需要做什麼來以無點式的方式編寫這個函數?

+3

我想你可能會對[$'和'.'運算符之間的差異感到困惑」(http://stackoverflow.com/questions/940382/haskell-difference-between-dot-and-dollar-sign )。 – hammar

回答

16

$具有非常低的優先級。所以,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是兩個操作,第一個分8的組合物,然後添加5到結果中。請記住,g.h表示「應用h,然後應用g的結果」。

+0

是的,現在有道理,謝謝! – KJ50

+0

這實際上很棒:所有三個upvoted答案都顯示了問題的不同角度。 –

10

「pointfree」程序可與cabal install pointfree一起安裝,並向您展示如何以無點式編寫表達式。例如:

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

這種轉換的說明:

  1. 您可以使用 「樂段」 中綴/操作功能。 (a +) == \b -> a + b(+ a) == \b -> b + a
  2. .函數接受第二個參數的結果,該參數是一個參數函數,並將其應用於第一個參數。
+0

我爲什麼要使用無點式樣? –

+0

我個人強迫自己使用無點式風格,因爲:1.我被迫使我的函數更簡單,因此使得它們更加可重用2.更有可能實際上我會費心重用已經寫好的函數,從而減少代碼重複。 – dflemstr

+1

請注意(。)是關聯性的,但($)不是,所以(。)爲重構提供了更大的靈活性。要使用(。),您需要掌握無點式樣。另外monadic combinators如liftM2,fmap,>> =和> =>幾乎迫使你學習無點式的風格。 – nponeccop

4

你真的很接近。允許我增加一個$來說明:

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

應當清楚的是,表達(+) 5是一個函數,它有一個數字輸入,併產生一個數字輸出。表達式(/) 8也是如此。因此,您需要輸入任何數字,x,並首先應用(/) 8「功能」,然後應用(+) 5「功能」。

只要你有由$分離功能鏈,你可以用.意味着所有的更換,除了最右邊的,如果你有a $ b $ c $ d,這相當於a . b . c $ d

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

在這一點上,我們實際上刪除$和圓括號來代替。現在

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

應該很清楚,你可以從兩邊去掉後x

f = (+) 5 . (/) 8 

是主要的想法。如果你有f x = expr x,你可以「eta減少」到f = expr。爲了生成無點代碼,您只需簡單地識別較大函數是如何由較小函數組成的。部分應用有時對於無點代碼是必需的(因爲在這種情況下,部分應用(+) 5(/) 8)。當你不想考慮它時,「無點」程序是非常有用的; #haskell irc頻道上的Lambdabot使用這個程序作爲插件,所以你甚至不需要自己安裝;只是要求:

<DanBurton> @pl let f x = 5 + 8/x in f 
<lambdabot> (5 +) . (8 /) 
10

從λ-演算(其Haskell是的一個變體)而言滑雪術語(完全pointfree功能,僅使用constķ)轉化率,id)和<*>小號))可以用下面的簡單的規則來完成:

  1. \x -> x轉化爲id;
  2. \x -> y沒有x發生在y轉換爲const y;
  3. \x -> f g轉化爲f' <*> g'其中
    • f'\x -> f
    • g'翻譯是\x -> g翻譯。

現在,你可能不知道從何在.有翻譯的最後的一個特例:如果f不具有x任何自由出現,然後\x -> f g轉化爲const 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) 

ETA-減少沒有必要完成翻譯,但是沒有它,我們會得到一些混亂。例如,最後一步將產生((+) 5) . ((/) 8) . id