我正在尝试编写我的unfoldr版本,以解决Haskell Book中的一章练习(第12章,信号逆境)。这本书说:


使用直接递归编写函数myUnfoldr。与之比较
内置的unfoldr来检查您的实现。再次,不要看
在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都可以正常工作

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

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

最佳答案

两个词:模式匹配。您两次使用$ 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章已经讲述了这两种技术。

关于haskell - 如何在Haskell中以更好/更惯用的方式编写我的unfoldr版本?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39309234/

10-10 11:43