2016-01-12 65 views
5

我想通過Haskell瞭解函數式編程,我在處理函數組合方面遇到了很多麻煩。有兩個參數的Haskell組合

其實我有這兩個功能:

add:: Integer -> Integer -> Integer 
add x y = x + y 

sub:: Integer -> Integer -> Integer 
sub x y = x - y 

我希望能夠撰寫他們。它沒有任何意義,但它是一個學習目標。

我已經試過:

foo:: (Integer -> Integer) -> (Integer -> Integer) -> Integer 
foo = add . sub 

就我的理解:

Haskell使用的功能只有一個指定參數時,讓我們回到一個新的功能後,每次執行函數執行。

因此,第一個Integer是參數類型,而第二個是生成函數的返回類型,它必須添加第二個數字。

這將返回另一個函數(sub),將使得同樣的流程(返回使用參數等功能...)

我說得對不對?

這裏是我的實際錯誤代碼:

src\Main.hs:23:7: 
    Couldn't match type `Integer' with `Integer -> Integer' 
    Expected type: Integer -> (Integer -> Integer) -> Integer 
     Actual type: Integer -> Integer -> Integer 
    In the first argument of `(.)', namely `add' 
    In the expression: add . sub 

src\Main.hs:23:13: 
    Couldn't match type `Integer -> Integer' with `Integer' 
    Expected type: (Integer -> Integer) -> Integer 
     Actual type: Integer -> Integer -> Integer 
    Probable cause: `sub' is applied to too few arguments 
    In the second argument of `(.)', namely `sub' 
    In the expression: add . sub 

我不知道我做錯了。

你能幫我理解更多這個錯誤,所以我可以找到解決方案嗎?

+5

你說得對,它「沒有任何意義」 - 有沒有在數學組成二元函數的概念。如果你想使它有意義,你需要首先定義什麼「構成」加法和減法手段。 – molbdnilo

+0

你的foo類型表示加法和減法的組合是一個函數,它接受兩個*函數*(整數 - >整數)並返回一個整數。 – molbdnilo

回答

6

給出一個函數

add :: Integer -> Integer -> Integer 

(如你在指出自己我的理解部)->式簽名同夥的權利,即上述類型是相同的是要記住有用作爲

add :: Integer -> (Integer -> Integer) 

現在考慮的(.)類型:

(.) :: (b -> c) -> (a -> b) -> a -> c 

這意味着,在以(.)的類型的表達式

(.) add 

b的是Integerc對應於Integer -> Integer。寫這另一種方式是

b ~ Integer 
c ~ Integer -> Integer 

所以我們得到

(.) add :: (a -> Integer) -> a -> (Integer -> Integer) 

如果現在申請(.) addsub,那a -> Integer不能作出編譯器會匹配Integer -> Integer -> Integer

我懷疑你可能要組成採取參數:兩到適用於sub,並且該結果是那麼 - 與第三個參數在一起 - 傳遞給add。因此,這構成了兩個函數可能的定義是

foo :: (Integer -> Integer -> Integer) -> (Integer -> Integer -> Integer) -> Integer -> Integer -> Integer 
foo f g x y = f (g x y) y 

對於它的價值,有一個相關的問題:組成一個雙參數函數有一個參數的功能,例如撰寫

+0

一個稍微不同的定義,foo f g x y = f(g x y)y'可以縮寫爲'foo = liftM2(。)'。 –

4

我希望能夠撰寫他們。它沒有任何意義,但它是一個學習目標。

,這裏其實就是問題所在。你想如何撰寫它們?讓我們來看看一些可能的組成:

foo x y = sub x (add x y)   -- x + y - x = y 
foo x y = sub y (add x y)   -- x + y - y = x 
foo x y = sub x (add y y)   -- 2 * y - x 
foo x y = sub y (add y y)   -- 2 * y - y = y 
foo x y = sub y (sub y (add x x)) -- 2 * x - 2 * y 

話雖這麼說,讓我們從手工檢查類型檢查錯誤類型:

type I = Integer -- otherwise the lines are going to be very long 

(.)   :: (b -> c ) -> (a -> b ) -> a -> c 
add   :: I -> (I -> I) 
sub   ::     I -> (I -> I) 
--        ||||||||||||| 
(.) add  ::     (a -> I ) -> a -> (I -> I) 
--        ^^^^^^^^^^^^^ 

正如你所看到的,(.) add已經強制要求其它功能可僅具有類型a -> Integer對於任意a。但是的sub類型是Integer -> (Integer -> Integer)(記住,(->)是右結合)。

現在,你能做些什麼真正解決這個問題?首先,讓我們檢查你提出的類型的foo

foo :: (Integer -> Integer) -> (Integer -> Integer) -> Integer 

這實際上是一個非常有趣類型的函數。你會如何得到你的結果?你只是手頭有兩個功能,但沒有值:

> foo f g = 

可以使用的功能之一的固定點解決這個,然後再應用其他:

> let x = f x in g x 
> 
> example = foo (const 12) (+1) -- returns 13 

但是,這不是你的意思,對吧?在這一點上,考慮作文的語義是非常重要的。由於這些不清楚,你不能在這裏寫出一個通用的方法來編寫這兩個函數。

不過,如果你實際上意味着

foo :: Integer -> Integer -> Integer -> Integer 
foo x y z = add (sub x y) z 

那麼這可能與

foo = (add .) . sub 

因爲

(.) add  :: (a -> I) -> a -> (I -> I) 
(.) ((.) add) :: (a -> b -> Integer) -> a -> b -> Integer -> Integer 

(add .) . sub是不是很容易的眼睛了。如果這種功能是您最初的目標,那麼您最好寫一個foo的明確定義。