[英]Haskell type magical world of Oz filtering nicta course functors and applicatives
[英]Filtering Applicatives
我正在做NICTA Haskell課程並且堅持使用Applicative
的最后一部分,這是我的進步。
基本上,我們將使用在Applicative上下文中生成List的謂詞來過濾List。
filtering :: Applicative f => (a -> f Bool) -> List a -> f (List a)
filtering _ Nil = pure Nil
filtering f (x :. rest) =
let whole = lift2 (:.) (pure x) (filtering f rest) in
lift2 filter (const <$> f x) whole
所以whole
這里是完整的未過濾列表,所以我解除了一個filter
,並且(const <$> fx)
添加的const是滿足filter
的(a -> Bool)
要求。
一切都很好,它編譯成功,但它失敗了一個測試用例,所以這里肯定是錯的。
例如, filtering (Id . even) [4,5,6]
只返回Id [4]
而不是Id [4,6]
(Id只是一個容器而且是一個Applicative。)
我在某個地方犯了什么錯誤嗎?
這里的問題是(const <$> fx)
。 該函數將用於檢查整個列表,但它只會在當前x
上提供f
的結果。 這意味着當它在[5,6]
子列表上工作時,它基本上是filter (const False) [5,6]
,這會產生一個空列表。
您無法調用filter
為您執行此操作,因為它的形狀不正確。 這樣的事總會發生。 相反,只需專注於包含當前元素,並讓遞歸正確處理列表的其余部分。 (我不想多說,因為這個課程的目標當然是為自己解決這個問題。)
我對你認為需要“完整的未經過濾的清單”感到困惑,我想你也是。 完整的未過濾列表x :. rest
x :. rest
,你已經擁有了!
顯然,你的意思是“整個的結果,因為這將是如果斷言總是得到True
...但是,這沒有任何意義,因為你基本預測,這將始終產生True
。
您需要考慮的是,而不是whole
事情,是兩個應用程序包裝的值:頭部謂詞的結果 - 這只是fx :: f Bool
。 並且,作為遞歸, 過濾剩余的列表 , filtering f rest :: f (List a)
。
現在,正如您已經注意到, liftA2
通常是組合兩個Applicative
-wrapped值的最簡單方法(盡管<*>
實際上傾向於為更整潔的代碼提供)。 召回
liftA2 :: (a->b->c) -> f a -> f b -> f c
在這種情況下,
liftA2 :: (Bool->List a->List a) -> f Bool -> f (List a) -> f (List a)
因此,您需要一個函數Bool->List a->List a
,如果布爾值為true,則前綴為x
,否則保留列表。 好吧,在本地where
塊中定義它應該不是問題。
這個問題要簡單得多,如果不是在一個函數中嘗試它,而是將它分解為更小的子問題。
我給出的第一個提示是它看起來非常像Traversable
類的一個問題,其中包括這個方法,它的簽名應該讓你“嗯!”:
traverse :: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b)
通過您問題中的搞笑List
類型,我收集您正在采取的課程,不允許使用庫函數來解決問題。 我不太同意這種做法; 我認為最好的學習方法是以下兩步過程:
所以我建議作為一個子問題,你應該為你的List
類型編寫自己的Traverse
版本。
下一個子問題:使用traverse
,編寫此函數應該很簡單:
tagWithBool :: (a -> f Bool) -> -> List a -> f (List (Bool, a))
一旦你有了這個,我要做的下一步就是編寫這個函數:
-- Remove the items tagged with `False`, and eliminate the tags.
removeFalse :: List (Bool, a) -> List a
同樣,我建議您在編寫時充分利用map
和filter
等實用程序功能,並在正確運行后,編寫自己的函數版本作為額外練習。 (實際上,考慮到問題中的非標准List
類型,你幾乎不得不編寫自己的版本來解決這個問題。)
一旦掌握了所有這些,您就可以寫:
filtering :: Applicative f => (a -> f Bool) -> List a -> f (List a)
filtering p xs = fmap removeFalse (tagWithBool xs)
請注意,這使用fmap
,因此您必須為List
類型實現Functor
(或者只是mapList :: (a -> b) -> List a -> List b
函數)。
這個怎么樣(我正在使用普通列表,而不是你的ctor)
seq f [] = []
seq x:xs = lift2 (:) x (seq xs)
remove (x:xs) (y:ys) = (if y then [x] else []) ++ remove xs ys
filterapp f xs = lift2 remove (pure xs) $ seq $ map f xs
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.