如何在 Haskell 中以更好/更惯用的方式编写我的 unfoldr 版本?
How to write my version of unfoldr in a better / more idiomatic manner in Haskell?
我正在尝试编写我的 unfoldr
版本以解决 Haskell Book 中的章节练习(第 12 章,逆境信号)。书上说:
Write the function myUnfoldr
using direct recursion. Compare with
the built-in unfoldr
to check your implementation. Again, don’t look
at implementations of unfoldr so that you figure it out yourself.
并给出以下内容:
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
按预期工作,returning []
.
我明白为什么我的 unFoldr
有问题,但我找不到如何处理 f
可以 return 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
上都能正常工作
但我对我的解决方案仍然不满意。
是否可以在不使用辅助函数的情况下以更简洁的形式编写它?或者以更惯用的方式?
两个字:模式匹配。您使用 $ 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 章都已经讲过了。
我正在尝试编写我的 unfoldr
版本以解决 Haskell Book 中的章节练习(第 12 章,逆境信号)。书上说:
Write the function
myUnfoldr
using direct recursion. Compare with the built-inunfoldr
to check your implementation. Again, don’t look at implementations of unfoldr so that you figure it out yourself.
并给出以下内容:
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
按预期工作,returning []
.
我明白为什么我的 unFoldr
有问题,但我找不到如何处理 f
可以 return 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
但我对我的解决方案仍然不满意。
是否可以在不使用辅助函数的情况下以更简洁的形式编写它?或者以更惯用的方式?
两个字:模式匹配。您使用 $ 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 章都已经讲过了。