我要編輯我的另一篇文章,但這是足夠大的自己。
下面是使用「類型魔法」進行處理的一種方法,但是我覺得它有點不太理想,因爲它需要特定於特定數量參數的函數的提升函數(詳見下文)。
讓我們首先定義一個遞歸的數據類型
data RecT a = RecR a
| RecC (a -> RecT a)
所以RECT類型變量可以是隻是包着一個結果(RECR),也可以是一個不斷遞歸(RECC)。
現在,我們如何採取措施並將其轉換爲RecT a類型?
價值觀很簡單:
valRecT x = RecR x
RECR x是類型RECT一個顯然。
如何使用一個參數,如id?
idRecT x = RecC $ \x -> RecR x
RecC包裝一個函數,該函數接受一個變量並返回類型RecT a。表達
\x -> RecR x
就是這樣一個功能,因爲當我們觀察到之前RECR x是類型RECT一個的。
更一般地,任何一個參數的功能,可以解除:
lift1RecT :: (a -> a) -> RecT a
lift1RecT fn = RecC $ \a -> RecR $ fn a
我們可以通過反覆包裝更深層嵌套函數概括這個調用裏面RECC:
lift2RecT :: (a -> a -> a) -> RecT a
lift2RecT fn = RecC $ \b -> RecC $ \a -> RecR $ fn b a
lift3RecT :: (a -> a -> a -> a) -> RecT a
lift3RecT fn = RecC $ \c -> RecC $ \b -> RecC $ \a -> RecR $ fn c b a
好了,我們已經完成了所有這些工作以將任意數量的參數的函數轉換爲單一類型RecT a。我們如何使用它?
我們可以很容易地寫下來的功能應用一層:
reduceRecT :: RecT a -> a -> RecT a
reduceRecT (RecC fn) = fn
reduceRecT _ = undefined
換句話說,reduceRecT需要類型的類型RECT一個的參數和另一併返回一個新RECT一個這一直是降低一個級別。
我們也可以展開一個矩形內完成計算到結果:
unrollRecT :: RecT a -> a
unrollRecT (RecR fn) = fn
unrollRecT _ = undefined
現在,我們已經準備好參數列表應用到的功能!
lApply :: [a] -> RecT a -> a
lApply [] fn = unrollRecT fn
lApply (l:ls) fn = lApply ls $ (reduceRecT fn) l
讓我們先考慮一下基本情況:如果我們完成了計算,我們只是打開結果並返回它。在遞歸的情況下,我們將參數列表減1,然後通過將列表頭部應用於簡化的fn來轉換fn,從而產生新的RecT a。
讓這給一試:
lApply [2,5] $ lift2RecT (**)
> 32.0
所以,優點和這種方法的缺點是什麼?那麼,模板Haskell版本可以執行部分列表應用程序;這不是真正的這裏提出的isorecursive類型的解決方案(雖然我們原則上可以用一些醜陋來解決這個問題)。類型解決方案還有一個缺點:有更多的樣板代碼與其相關聯:我們需要一個listNRecT用於我們想要使用的所有N.最後,如果我們希望將其應用於混合變量類型的函數,那麼將其推廣到類比元組解決方案並不那麼容易。
當然,另一個有趣的可能性是通過使用Template Haskell來生成listNRecT函數來增強簡潔性;這消除了一些樣板,但從某種意義上來說,這兩種實現都有缺點。
快速瀏覽polyvariadic函數,但它可能會打擊你的思想:[http://stackoverflow.com/questions/3467279/how-to-create-a-polyvariadic-haskell-function] – fuz 2010-09-23 11:51:56