我试图编写我的unfoldr
版本,以解决Haskell Book(第12章,信号逆境)中的章节练习。这本书说:
使用直接递归编写函数
myUnfoldr
。与之比较 内置unfoldr
来检查您的实现。再说一遍,别看 在展开的实现,以便你自己解决。
并提供以下内容:
myUnfoldr :: (b -> Maybe (a, b)) -> b -> [a]
myUnfoldr = undefined
我对myUnfoldr
的初步尝试是使用单个辅助函数编写它:
maybeToList :: Maybe a -> [a]
maybeToList (Just x) = [x]
maybeToList Nothing = []
myUnfoldr :: (b -> Maybe (a, b)) -> b -> [a]
myUnfoldr f x = (fst $ head $ maybeToList (f x)) :
myUnfoldr f (snd $ head $ maybeToList (f x))
此类型检查,并且对于一些示例运行,似乎我已经实现unfoldr
所做的事情,因为:
λ> take 10 $ unfoldr (\x -> Just (x, x + 1)) 0
[0,1,2,3,4,5,6,7,8,9]
与myUnfoldr
相同的结果:
λ> take 10 $ myUnfoldr (\x -> Just (x, x + 1)) 0
[0,1,2,3,4,5,6,7,8,9]
但当然,myUnfoldr
是有问题的,因为如果我尝试以下内容:
λ> take 10 $ myUnfoldr (\x -> Nothing) 0
[*** Exception: Prelude.head: empty list
而unfoldr
按预期工作,返回[]
。
我可以看到为什么我的unFoldr
存在问题,但我无法了解如何处理f
可以返回Nothing
的情况。
所以我对这个问题的第二个看法是使用另一个辅助函数,这次使用的是守卫:
isJust :: Maybe a -> Bool
isJust (Just _) = True
isJust _ = False
myUnfoldr :: (b -> Maybe (a, b)) -> b -> [a]
myUnfoldr f x | isJust $ (f x) = (fst $ head $ maybeToList (f x)) : myUnfoldr f (snd $ head $ maybeToList (f x))
| otherwise = []
第二个版本似乎与take 10 $ myUnfoldr (\x -> Just (x, x+1)) 0
和take 10 $ myUnfoldr (\x -> Nothing) 0
但我仍然不满意我的解决方案。
是否可以使用辅助函数以更简洁的形式编写任何想法?还是以更惯用的方式?
答案 0 :(得分:4)
两个词:模式匹配。您使用$ head $ maybeToList (f x)
两次来获取下一个状态以及列表元素。但是,我们可以在f x
上进行模式匹配,以确定我们是否有Just
另一个元素(以及继续我们的小展开的状态)或者我们是否已完成:
myUnfoldr :: (s -> Maybe (a, s)) -> s -> [a]
myUnfoldr f x = case f x of
Just (next, state) -> next : myUnfoldr f state
Nothing -> []
您的变体存在的问题是您没有使用isJust (f x)
。而不是检查某些东西是否为Just
然后从中获取值,只需检查该模式是否匹配。
请注意,图案防护也可以这样做:
myUnfoldr :: (s -> Maybe (a, s)) -> s -> [a]
myUnfoldr f x
| Just (next, state) <- f x = next : myUnfoldr f state
| otherwise = []
我还没有读过这本书,但我想第12章已经说过这两种技术。