delete
是模態搜索。它有兩種不同的操作模式 - 無論它是否已經找到結果。您可以使用foldr
構造一個函數,在檢查每個元素時將狀態傳遞給行。因此,在delete
的情況下,狀態可以是簡單的Bool
。這不是最好的類型,但它可以。
一旦確定了狀態類型,就可以開始使用foldr
構造。我將通過我的方式來解決問題。我將啓用ScopedTypeVariables
,以便我可以更好地註釋子表達式的類型。一個你知道狀態類型,你知道你想要foldr
生成一個函數獲取該類型的值,並返回一個期望的最終類型的值。這足以開始草繪東西。
{-# LANGUAGE ScopedTypeVariables #-}
delete :: forall a. Eq a => a -> [a] -> [a]
delete a xs = foldr f undefined xs undefined
where
f :: a -> (Bool -> [a]) -> (Bool -> [a])
f x g = undefined
這是一個開始。 g
的確切含義在這裏有點棘手。它實際上是處理列表的其餘部分的函數。事實上,把它看作是一個延續是準確的。它絕對代表了您執行摺疊的其餘部分,無論您選擇傳遞什麼狀態。鑑於此,現在是時候弄清楚在undefined
的某些地方放些什麼。
{-# LANGUAGE ScopedTypeVariables #-}
delete :: forall a. Eq a => a -> [a] -> [a]
delete a xs = foldr f undefined xs undefined
where
f :: a -> (Bool -> [a]) -> (Bool -> [a])
f x g found | x == a && not found = g True
| otherwise = x : g found
這似乎相對簡單。如果當前元素是正在搜索的元素,但尚未找到,請不要輸出它,並繼續設置爲True
的狀態,表示已找到該元素。 otherwise
,輸出當前值並繼續當前狀態。這隻剩下參數foldr
。最後一個是初始狀態。另一個是空列表的狀態函數。好的,那些也不算太壞。
{-# LANGUAGE ScopedTypeVariables #-}
delete :: forall a. Eq a => a -> [a] -> [a]
delete a xs = foldr f (const []) xs False
where
f :: a -> (Bool -> [a]) -> (Bool -> [a])
f x g found | x == a && not found = g True
| otherwise = x : g found
無論狀態是什麼,遇到空列表時都會產生一個空列表。初始狀態是正在搜索的元素尚未找到。
該技術也適用於其他情況。例如,foldl
可以這樣寫爲foldr
。如果您將foldl
看作一個重複轉換初始累加器的函數,那麼您可以猜測這是正在生成的函數 - 如何轉換初始值。
{-# LANGUAGE ScopedTypeVariables #-}
foldl :: forall a b. (a -> b -> a) -> a -> [b] -> a
foldl f z xs = foldr g id xs z
where
g :: b -> (a -> a) -> (a -> a)
g x cont acc = undefined
基座情況都不是太棘手問題時被定義爲操縱所述初始累積器,命名爲z
那裏找到。空列表是標識轉換,id
,傳遞到創建的函數的值是z
。
g
的執行更爲棘手。它不能只是盲目地在類型上完成,因爲有兩種不同的實現使用所有期望的值和類型檢查。這種情況下類型不夠,你需要考慮可用函數的含義。
讓我們從看起來應該使用的值及其類型的清單開始。看起來像他們必須在g
正文中使用的東西是f :: a -> b -> a
,x :: b
,cont :: (a -> a)
和acc :: a
。 f
明顯地將x
作爲第二個參數,但是存在使用cont
的適當位置的問題。要確定它的位置,請記住它表示處理列表的其餘部分返回的轉換函數,並且foldl
處理當前元素,然後將處理結果傳遞給列表的其餘部分。
{-# LANGUAGE ScopedTypeVariables #-}
foldl :: forall a b. (a -> b -> a) -> a -> [b] -> a
foldl f z xs = foldr g id xs z
where
g :: b -> (a -> a) -> (a -> a)
g x cont acc = cont $ f acc x
這也表明,foldl'
可以這樣寫,只有一個微小的變化:
{-# LANGUAGE ScopedTypeVariables #-}
foldl' :: forall a b. (a -> b -> a) -> a -> [b] -> a
foldl' f z xs = foldr g id xs z
where
g :: b -> (a -> a) -> (a -> a)
g x cont acc = cont $! f acc x
不同的是,($!)
在使用之前它傳遞給cont
暗示的f acc x
評價。 (我說的「建議」,是因爲有一些特殊的情況($!)
不會強制評估甚至遠WHNF。)
我很欣賞你的答案並發現它很有用。如果我有足夠的聲望,我會加倍努力,但我不接受,因爲你的回答不太符合我的要求。但是,這是我的錯,因爲沒有更精確地提出問題,我會看看我是否可以編輯。我對使用foldr生成函數的方法更感興趣。你說得對,你的第一個實現幾乎是我的嘗試。 – user168064 2014-12-13 08:20:06
@ user168064好的。花了大約3個小時的時間來學習這個話題之後,我也可以回答你的預期問題。我認爲它應該是第二個答案,而不僅僅是編輯這個答案,所以另一個答案是傳入的。 – Carl 2014-12-13 11:31:50