2013-03-27 82 views
8

我試圖通過編寫使用他們幾個簡單的功能,以瞭解在Haskell lambda函數(即匿名函數)。錯誤而宣告lambda函數:聲明一個實例第一

在下面的例子中,我只是想取3個參數,並使用匿名函數添加兩三個,加入後的第三個參數。 我收到一個錯誤說,我首先聲明一個實例。

specialAdd x y z = (\x y -> x + y) + z 

我很感謝任何解釋爲什麼我的例子不工作和/或任何解釋,這將有助於我更好地理解lambda函數。

+2

(\ XY - > X + Y)::貨號一個=>一 - >一 - > a 您嘗試將z添加到功能中 – Vladimir 2013-03-27 13:40:22

回答

16

specialAdd x y z = (\x y -> x + y) + z

在這個例子中,你正在嘗試做的是增加一個功能,一個數字,這是行不通的。看看(\x y -> x + y) + z:它的形式是a + b。爲了使這樣的表達起作用,a部分和b部分必須是相同類型的數字。

Haskell是一種不尋常的語言,所以它的錯誤信息很少是「你不能這樣做」的形式。所以這裏發生的是Haskell認爲(\x y -> x + y)是一個函數,並且因爲在像a + b,b這樣的表達式中必須是與a相同的類型,所以它得出結論b也必須是函數。 Haskell還允許您定義自己的添加非內置類型的規則;所以它不能只給你一個錯誤,說「你不能添加兩個函數」,而是錯誤是「你還沒有定義一個允許我添加兩個函數的規則」。

下面會做你想要什麼:

specialAdd x y z = ((\x y -> x + y) x y) + z 

在這裏,你所申​​請的功能(\x y -> x + y)將參數xy,然後把結果加到z

1

您正在嘗試使用(+)的東西像(Num a) => (a -> a -> a) -> a -> ??這是不正確的。

(+)在類Num中定義,並且(a→a→a)不是該類的一個實例。

究竟是你想達到什麼目的?

10

練習匿名函數的好方法是用高階函數作爲倍或地圖中使用它們。

使用的地圖爲切入點,

地圖的基本定義,

map f [] = [] 
map f (x:xs) = f x : f xs 

建立了一個例子,

>>> let list = [0..4] 
>>> let f x = x + 1 

運用地圖,我們得到,

>>> map f list 
[1,2,3,4,5] 

現在,我們可以省略t他F的聲明,並使用匿名函數代替它,

>>> map (\x->x+1) list 
[1,2,3,4,5] 

然後我們推斷,映射f列表==地圖(\ X-> X + 1)列表,從而

f = \x-> x + 1 --- The same as f x = x + 1, but this is the equivalent lambda notation. 

然後從一個簡單的函數開始,我們看到如何將其轉換爲匿名函數,然後如何匿名函數可以依賴於lambda抽象。

作爲練習試着翻譯˚FX = 2 * X。

現在越來越複雜,一個匿名函數這兩個參數,

同樣的工作示例,

>>> let add x y = x + y 
>>> foldl' add 0 [0..4] 
10 

可以是重寫使用匿名功能,

>>> foldl' (\x y -> x + y) 0 [0..4] 

再次使用平等我們推斷add = \ xy - > x + y
此外,在hakell中,所有函數都是一個參數的函數,我們c一個部分應用它,我們可以將我們以前的匿名函數重寫爲:add = \ x - >(\ y - > x + y)。

那麼訣竅在哪裏?因爲,我只是將匿名函數用於高級函數,並從中開始介紹如何利用lambda表示法重寫函數。我的意思是它如何幫助你學習如何寫下匿名函數?

簡單地說,我已經給你(顯示你)使用高階函數的現有框架。
這個框架是一個巨大的機會,以適應你這個表示法。
從可以推斷無限範圍的運動開始,例如嘗試執行以下操作。

A - Find the corresponding anonymous function ? 

1 - let f (x,y) = x + y in map f [(0,1),(2,3),(-1,1)] 
2 - let f x y = x * y in foldl' f 1 [1..5] 

B - Rewrite all of them using lambda notation into a declarative form (f = \x-> (\y-> ...) 

等等....


總之,

一個功能

(F0) f x1 x2 ... xn = {BODY of f} 

總是可以被改寫爲,

(F1) f = \x1 x2 ... xn -> {BODY of f} 

其中

(F2) (\x1 x2 ... xn -> {BODY of f}) 

F2形式都只是匿名函數,該函數的純翻譯成演算的形式。 F1是一個聲明性的lambda符號(因爲我們聲明f,就像我們定義它,將它綁定到匿名F2)。 F0是Haskeller的通常記號。

最後一個注意事項我們可以在參數之間放置括號,這會創建一個閉包。這意味着函數代碼的一個子集可以使用該函數參數的子集進行全面評估(意味着轉換爲不再有自由變量的形式),但這是另一回事。

2

從我的理解Labmbda /匿名函數可以幫助您聲明一個函數「內聯」而無需給它一個名字。 「\」(希臘語的ASCII,λ)位於「 - >」後面的表達式的變量名稱前面。例如,

(\x y -> x + y) 

是一個類似於(+)的匿名(lambda)函數。這需要類型民的兩個參數,並返回它們的總和:

Prelude> :type (+) 
(+) :: Num a => a -> a -> a 

Prelude> :type (\x y -> x + y) 
(\x y -> x + y) :: Num a => a -> a -> a 

因爲,正如其他人所指出的那樣,它的右側是使用lambda函數您的例子不工作,(\ XY - > X + y)作爲(+)運算符的參數,默認情況下僅爲Num類型的參數定義。 lambda函數的一些優點可以在其「匿名」使用中。弗拉基米爾展示瞭如何通過從左側傳遞變量來在聲明中使用lambda函數。一個更「匿名」的用法可以簡單地用變量來調用它,而不給函數一個名字(因此是匿名的)。例如,

Prelude> (\x y z -> x + y + z) 1 2 3 
6 

and if you like writing parentheses: 

Prelude> (((+).) . (+)) 1 2 3 
6 

或者在更長的表達(如在你的例子聲明),例如,

Prelude> filter (\x -> length x < 3) [[1],[1,2],[1,2,3]] 
[[1],[1,2]]