如何在Haskell中以更好/更惯用的方式编写我的unoldr版本?

时间:2016-09-03 16:49:46

标签: haskell fold

我试图编写我的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)) 0take 10 $ myUnfoldr (\x -> Nothing) 0

一样正常工作

但我仍然不满意我的解决方案。

是否可以使用辅助函数以更简洁的形式编写任何想法?还是以更惯用的方式?

1 个答案:

答案 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章已经说过这两种技术。