星期六, 10月 04, 2008

Haskell Learning Note(3)

試論curring

在Haskell中,不用C++這麼複雜的方法來解決問題,而是採用curried function來解決這個問題。

Prelude> let add x y = x + y   -- 簡單來說就是寫一個 (+)
Prelude> :t add
add :: (Num a) => a -> a -> a -- 此時的add是一個具有兩個引數的function
Prelude> :t add 1
add 1 :: (Num t) => t -> t -- 此時的add 1 是具有一個引數的function
Prelude> :t add 1 2
add 1 2 :: (Num t) => t -- 此時的add 1 2是一個不具有引數的function

在Haskell裡,使用單純的curried function即可解決,寫起來很像C++的function default arguemnt,意義不太一樣,在Haskell裡是真的把引數代入形成一個新的function,其實+ operator也可以做出同樣的功能(Josh Ko說這是語法糖XD)

Prelude> :t (+)
(+) :: (Num a) => a -> a -> a
Prelude> :t (1+)
(1+) :: (Num t) => t -> t
Prelude> :t (1+2)
(1+2) :: (Num t) => t

寫到這裡我在想,我幹麻沒事自己寫一個add呢 XD那麼,可以試著用map寫出一點有趣的東西

Prelude> map (1+) [1..10]
[2,3,4,5,6,7,8,9,10,11]

當然,一個curried function在FP中的功用不止於此,curried funciton可以做更多事,隨你寫,隨你造,而不用C++ STL裡的bind2nd這種相較之下較不直覺的方法。curried fuction的好處是在Haskell底下是lazy evaluation,也就是說,你function引數可以不用給全,直到你要求把答案算出來的那一刻把argument給全就行了,而且可以隨你組合,隨你寫,可以做出相當多的靈活變化,也就是說,curried function並非要求一次把function arguement給齊,在Haskell底下大部分的funciton都是curried function。

那麼有沒有要求要一次給齊的,換個腦子想想,就是uncurried function,在這樣子的一個function底下,沒有一次給足所有的argument就會出錯(一時想不到有什麼function)

補充:當我們寫(1+)而我們又呼叫這個function時,是類似C++的default argument還是wrapper function的技法呢,如果記得是沒有錯,是前者,也就是說,不管怎麼compose,都是只有一層的呼叫,相當的方便。


---
真糟,寫的真少。

3 則留言:

Josh Ko 提到...

> uncurried function [...] 沒有一次給足所有的argument就會出錯

要看是怎麼定義「出錯」嘍。純論 Haskell 的話是不會出錯的,沒給足引數就還只是函式,必須再給完剩下的引數才進行求值(或是只能部份求值)。

yen3 提到...

謝謝~:)
那麼顯然我會再多看書再進行論述的

T55555 提到...

In fact, we can say all Haskell functions accept one and only one input argument
and return one and only one output. ( There is no such thing about multi-arguments :-) )

Because a function f like:

f :: a -> b -> c -> d
is exactly the same as
f :: a -> (b -> (c -> d))
This is because -> is right associative.

So, when you write
f w x y z
it is the same as
(((f w) x) y) z


And "partial" arguments means the output will always be a function (instead of a value).

~~~~~~~~~~~~~~~~~

Lazy evaluation has nothing to do with function arguments.
Lazy evaluation is about evaluation when needed.

So, even you gave "all" arguments for a function,
it may still not evaluate to final result if there is no needed for the result.

Same, when you gave "partial" arguments, it may already evaluated (to return function) if needed.

The easy way to see this is via ghci:

Prelude> let x = (+) 1 2
Prelude> :show bindings
x :: Integer = _
Prelude> x + 5
8
Prelude> :show bindings
it :: Integer = 8
x :: Integer = 3
Prelude>

As you can see for x = 1 + 2, we gave all arguments to (+) but
the final result is still not evaluated.
It is only when we try x + 5 to force evaluate x.
You can see the difference on both show bindings about x.