我無法理解curried和uncurried函數。我谷歌的所有網站都試圖爲我提供一個定義,但對我而言並不清楚。不安全的功能
在一個例子中,我發現他們說
max 4 5
相同(max 4) 5
但我不明白他們在做什麼。當max需要2個參數時,如何獲得(max 4)
功能?我完全失去了。
我無法理解curried和uncurried函數。我谷歌的所有網站都試圖爲我提供一個定義,但對我而言並不清楚。不安全的功能
在一個例子中,我發現他們說
max 4 5
相同(max 4) 5
但我不明白他們在做什麼。當max需要2個參數時,如何獲得(max 4)
功能?我完全失去了。
Haskell的訣竅是函數只有一個參數。這看起來非常瘋狂,但它確實有效。
一個Haskell函數:
foo :: Int -> Int -> Int
foo a b = a + b
真正含義是:函數,它接受在1個參數,然後返回另一個函數,它帶有一個參數。這叫做咖喱。
所以用這個,我們真的可以寫這個函數的定義是這樣的:
foo :: Int -> (Int -> Int) --In math speak: right associative
,並意味着完全一樣的東西。
這實際上是超級有用的,因爲我們現在可以編寫簡潔的代碼,如:
foo1 :: Int -> Int
foo1 = foo 1
由於在Haskell功能的應用僅僅是空白,大部分的時間你可以假裝咖喱功能uncurried(採取更多比一個參數還要返回結果)。
如果你確實真的需要uncurried函數:使用元組。
uncFoo :: (Int, Int) -> Int
uncFoo (a, b) = a + b
編輯
行,所以要了解怎麼回事與部分應用程序考慮bar
bar a b c = [a, b, c]
的事情是,編譯器將desugar你剛纔輸入到這樣
lambda表達式bar = \a ->
\b ->
\c ->
[a, b, c]
這利用了閉包(每個內部函數都可以「記住」以前的參數。
所以當我們說bar 1
,編譯器去,並看着bar
,看到最外層的λ,並將其應用於給
bar 1 = \b ->
\c ->
[1, b, c]
如果說bar 1 2
bar 1 2 = \c ->
[1, 2, c]
如果我是什麼意思時,我說「應用」是模糊的,那麼它可能有助於知道我真的是從lambda演算中得到beta reduction。
根據您的背景,您可能會發現本文有啓發性:How to Make a Fast Curry: Push/Enter vs Eval Apply。雖然多參數函數可以理解爲綁定單個參數並返回另一個函數的函數:max = (\a -> (\b -> if a > b then a else b))
,但實際實現效率更高。
如果編譯器靜態地知道max
需要兩個參數,編譯器將始終通過推入堆棧(或寄存器中)的兩個參數來轉換max 4 5
,然後調用max
。這與C編譯器如何翻譯max(4, 5)
基本相同。另一方面,如果例如max
是更高階函數的參數,則編譯器可能不會靜態知道max
需要多少個參數。也許在一個例子中,它需要三個,所以max 4 5
是一個部分應用程序,或者也許在另一個它只需要一個和max 4
生成一個新的功能,應用5
。本文討論了處理不確定靜態的情況的兩種常見方法。
涉及到你自己的例子......
假設您想要一個函數,給出了最大的4和功能參數。您可以實現這樣的:
max4 :: Integer -> Integer
max4 x = max 4 x
max 4
做什麼只是返回動態創建功能max4
。
你可能有已在您的回答,只是重申:
如果我們有
add x y = x + y
那麼我們可以說以下內容:
add = \ x y -> x + y
add 3 = \ y -> 3 + y
add 3 5 = 3 + 5 = 8
你問「怎麼會max 3
計算什麼?「,答案是」它不能「。它只是給你另一個函數。這個函數可以在你調用它的時候做一些事情,但是你不會「得到一個答案」,直到所有的參數都被提供。在此之前,你只需要獲取函數。
大多數時候,這僅僅是一個有用的語法捷徑。例如,你可以寫
uppercase :: String -> String
uppercase = map toUpper
,而不必說
uppercase xs = map toUpper xs
注意,如果map
有其他方式的爭論,我們就無法做到這一點(你只能咖喱了最後論點,而不是_first),所以重要的是要考慮你定義你的函數的論點的順序。
我說「大部分時間」,因爲這不僅僅是語法糖。在語言中有幾個地方可以使用不同數量的參數來處理函數多態性,因爲它是柯里化的。每個函數都會返回一個答案或另一個函數。如果你認爲它像一個鏈表(它包含下一項數據或列表結束標記),你可以看到這是如何遞歸處理函數的。
那麼究竟發生了什麼我的意思了?嗯,比如說,快速檢查可以測試功能與任何數量的參數(提供有一種方法來自動生成每個參數的測試數據)。這是可能的,因爲功能類型是咖喱。每個函數都會返回另一個函數或結果。如果你想像鏈表一樣,你可以想象QuickCheck遞歸迭代函數,直到沒有更多的參數被留下。
下面的代碼片段可能會或可能無法解釋的想法:
class Arbitrary a where
autogenerate :: RandomGenerator -> a
instance Arbitrary Int
instance Arbitrary Char
...
class Testable t where
test t :: RandomGenerator -> Bool
instance Testable Bool where
test rnd b = b
instance (Arbitrary a, Testable t) => Testable (a -> t) where
test rnd f = test $ f (autogenerate rnd)
如果我們有一個功能foo :: Int -> Int -> Bool
,那麼這是Testable
。爲什麼?那麼,Bool
是可測試的,因此這樣的Int -> Bool
,因此這樣是Int -> (Int -> Bool)
。
相反,元組的每個尺寸是不同的大小,所以你必須寫爲每個和元組的每一個單獨的大小的函數(或實例)。你不能遞歸地處理元組,因爲它們沒有遞歸結構。
參見[什麼是譁衆取寵的優勢在哪裏?(http://programmers.stackexchange.com/q/185585/61231)。 – 2013-03-21 05:48:00
我想這個問題,我的答案可能有幫助:http://stackoverflow.com/questions/8148253/how-are-functions-curried/8148957 – Ben 2013-03-21 05:54:03