tl;dr 这是你的功能。但我建议您继续阅读以了解我是如何想到这一点的,这样您就可以制定出思考过程。
newbmap :: (a->b)-> Newb a -> Newb b
newbmap f = foldNewb (Leaf . f) Node
你已经很接近了,你正确地使用了foldNewb,但你想多了。
首先,您不能将函数命名为 Newbmap。大写名称是为类型保留的。所以我们称之为newbmap。现在,foldNewb 已经处理了Leaf 和Node 这两种情况,所以我们根本不需要在newbmap 中进行模式匹配。事实上,您的第一个 newbmap 案例与 foldNewb 所做的完全一样,所以我们只考虑第二种情况。
newbmap :: (a->b)-> Newb a -> Newb b
newbmap f (Node a) = (Node (foldNewb f concat a))
我们想要折叠我们的数据结构。特别是,我们希望我们的 fold 调用能够完全生成新的数据结构。我们不应该需要在最后明确使用Node,因为foldNewb 已经为我们工作了。
newbmap :: (a->b)-> Newb a -> Newb b
newbmap f a = foldNewb f concat a
现在在第一种情况下,我们需要一个函数a -> Newb b(因为结果将是Newb b 类型)。你已经通过了f :: a -> b,它非常接近你想要的。我们只需要用函数b -> Newb b 组合它,Leaf 就可以做到这一点。
newbmap :: (a->b)-> Newb a -> Newb b
newbmap f a = foldNewb (Leaf . f) concat a
对于第二个参数,您需要[Newb b] -> Newb b,Node 也很容易做到这一点。
newbmap :: (a->b)-> Newb a -> Newb b
newbmap f a = foldNewb (Leaf . f) Node a
而且(虽然它没有什么区别),我们可以将最后一个论点放空。
newbmap :: (a->b)-> Newb a -> Newb b
newbmap f = foldNewb (Leaf . f) Node
所以有一个有效的newbmap 函数。现在,至于我是如何提出所有这些类型的,如果您使用 GHC,有一个非常有用的功能,称为 typehole,您可以使用它来确定您需要哪种类型。所以(我在调试你的函数时正是这样做的)如果你写
newbmap :: (a->b)-> Newb a -> Newb b
newbmap f = foldNewb _1 _2
然后您会收到非常具体的 GHC 消息,告诉您 _1 :: a -> Newb b 和 _2 :: [Newb b] -> Newb b。那么您的挑战就是找到具有这些特定类型的函数。这就是我想出Leaf . f 和Node 的地方。