2011-07-22 56 views
3

我做在以下幾個方面列出了連接(例如,使用GHC):哈斯克爾函數定義語法

myConcat :: [[a]] -> [a] 
myConcat xs = foldr (++) [] xs 
myConcat = foldr (++) [] 

有人能向我解釋,請爲何以及如何對上述定義工作,這其中不:

myConcat xs = foldr (++) [] 

是最後一行代碼故意不允許的(是有原因的,如結構可能會變得混亂,它是無用的,等等),或者是更深層次的東西,可能與討好......

我希望我能提供一些線索這光線,這真的讓我爲難:/

以後編輯:除了下面給出的說明,我已經找到了一個很好的信息源上的事是從章一節"Partial function application and currying"。書籍"Real World Haskell"中的4「函數式編程」。這本書可以在網上免費下載。

回答

7

讓我們回顧不同的版本:

myConcat xs = foldr (++) [] xs 

這是通常的方式,提供一個參數,它是由foldr消耗。類型爲[[a]] -> [a],因爲我們在左側有一個類型爲[[a]]的參數,當輸入到右側時產生[a]

myConcat = foldr (++) [] 

這裏foldr被部分應用,所以我們回到這可能需要一個額外的參數,列表列表的功能。所以我們從右邊返回的東西已經是我們需要的東西了,它不是一個「語法suger」,而是表達與第一個版本相同的東西的另一種方式。該類型再次爲[[a]] -> [a]:我們在左側沒有任何內容,但在右側返回該簽名的功能。

myConcat xs = foldr (++) [] 

這裏foldr被部分應用,以及,我們返回,可以採取一個參數作爲前一個功能,但是我們的定義有一個額外的參數xs,這是不是在右側使用。編譯器不會「知道」這個這個我們想要應用於右側的參數。該類型是t -> [[a]] -> [a]。爲什麼?

假設你有一個平方函數:

sqr :: Int -> Int 
sqr x = x*x 

你在做什麼本質上是一樣提供另外,未使用的參數:

sqr:: Int -> t -> Int 
sqr x y = x*x 

功能仍然是「作品」,如sqr 3 "bla"得出9,但類型簽名關閉,未使用的參數是...... erm,未使用。沒有使用的論點沒有固定的類型,因爲它幾乎可以是「任何東西」,沒關係。所以它在簽名中獲得類型變量(t)。

+0

+1:一個非常容易理解的解釋;你碰巧有一個Haskell相關的博客嗎?我喜歡你的風格。 :-) –

+0

@Frerich Raabe:謝謝!事實上,我有一個博客,主要是關於Scala,不過現在又是Haskell,但不幸的是它是德文的:http://dgronau.wordpress。com/ – Landei

+0

非常感謝你,我現在明白了:)很好的解釋 – Adi

2
myConcat xs = foldr (++) [] 

具有類型t -> [[a]] -> [a]這是不相同的其它兩個[[a]] -> [a]類型。

3

好吧,讓我們來看看爲curried functionfoldr類型簽名:

>:t foldr 
foldr :: (a -> b -> b) -> b -> [a] -> b 

所以foldr取一個二元函數(即a->b->b),一個b值,a值的列表,並返回一個值爲b

讓我們也看看documentationfoldr以獲得更明確的定義:

foldr相似,適用於二元運算,初始值(操作者通常是 右身份),以及列表中,降低了使用 二元運算符的列表中,由右至左:

現在,讓我們來看看類型簽名myConcat xs = foldr (++) []

> :t myConcat 
myConcat :: t -> [[a]] -> [a] 

嗯......這不是我們想要的...

的問題是,你永遠不提供foldr[a]類型的值。所以,現在,myConcat需要一定的價值,任何類型,以滿足xs[a]類型的值來完成foldr (++) [],如:

> myConcat 2 [[1,2],[3,4]] 
[1,2,3,4] 
> myConcat Nothing [[1,2],[3,4]] 
[1,2,3,4] 

這樣的作品,但第一個參數是太浪費了。

然而,如果我們傳遞xs值到foldr (++) [],如:

myConcat xs = foldr (++) [] xs

,並檢查它的類型簽名

> :t myConcat 
myConcat :: [[a]] -> [a] 

嗯,好多了。現在myConcat使用xs來完成foldr函數。

另外,myConcat = foldr (++) []也有效,實際上是point-free style programming的一個例子。如果我們檢查既然我們已經通過partial application提供foldr它的前兩個參數的foldr (++) []類型簽名,

> :t foldr (++) [] 
foldr (++) [] :: [[a]] -> [a] 

,我們得到了一個功能回到那個意志需要[[a]]值,做我們想要的!所以我們只是將它分配給一個名稱,它的工作方式就像上面的例子,但我們不需要明確地傳遞參數!

> let myConcat = foldr (++) [] 
> :t myConcat 
myConcat :: [[a]] -> [a] 
> myConcat [[1,2],[3,4]] 
[1,2,3,4] 
+0

謝謝你的解釋,我真的很感謝你的幫助:) – Adi