我们可以这样定义二叉树:
data Tree a = Node a (Tree a) (Tree a)
| Empty
deriving (Show)
现在我有了一个函数,比如(+)
,我怎么能把它映射到二叉树?如何将任意函数映射到二叉树?
所以(+) a b
将是
Node (+) a (Node (+) Empty Empty) (Node a Empty Empty))
这是一个要求我们将功能映射到树上的作业,以上就是我对它的理解 函数声明可以是:
functionToTree :: (t1 -> t2) -> Tree a
我坚持定义一个参数数量可变的函数类型。
修改:
对不起,正如nponeccop所说,我误解了我的任务,我知道如何写functionToTree :: (a -> b) -> Tree a -> Tree b
。尽管我仍然对我原来的问题感到好奇。
为了澄清一点,这就是我一直在想的:
(+) a
/ \
(+) a
至于功能f
采用三个参数a
b
c
:
f a b
/ \
f a b
/ \
f a
好的,这不再是我的作业了。我只是想知道是否可以编写这样的函数。 我们可以用这样的方式定义一个列表:
data Li a = Cons a (Li a)
| Empty
deriving (Show, Eq, Order)
是否可以像这样定义一般功能? 如果您认为我的问题毫无意义,那么请将其投票。
更多信息:我提炼了我的问题。我认为我的树是另一种说明部分功能和咖喱的方法。
答案 0 :(得分:1)
你对这项任务并不了解。在树上映射函数是将函数应用于树中包含的每个数据元素。
我建议你先画一个包含数字的树。
1
/ \
2 3
\ / \
4 6 5
您可以使用Tree a
数据类型对此树进行编码,即将树编写为包含Node
和Empty
构造函数的表达式吗?
以下是一些提示:
顶部节点包含1
和两个非空子树。
左子树包含2
,一个空子树和一个非空子树。
右子树包含3
和两个非空子树。
三级节点都是具有空子树的树。
编辑帖子以在那里插入示例树。一旦您告诉我们您可以这样做,我们可以帮助您继续前进。
你错了你的树。正确的树是: f 1
/ \
f 2 f 3
\ / \
f 4 f 6 f 5
因此,您只能使用一个参数映射函数,但不能映射两个,三个或更多。
这个想法是你可以(例如)为树的每个元素添加两个。所以,如果你通过
1
/ \
2 3 and (\x -> x + 2) or equivalently (+2)
\ / \
4 6 5
到您的功能,例如tree2 = functionToTree (+2) tree1
,你得到修改后的树:
3
/ \
4 5
\ / \
6 8 7
因此树的每个元素都被一个新元素替换。
答案 1 :(得分:0)
鉴于功能数据结构(在本例中为树),通常可以使用两种常规方法。
映射是您获取函数f :: a -> b
和结构origTree :: Tree a
的位置,并将函数应用于结构的元素,从而生成newTree :: Tree b
。 (请注意,使结构可映射的规范方法是使其成为Functor
,并定义fmap
)
Folding 是您以某种方式将结构的所有元素组合成一些新值的地方。当你说你有一个Tree
和(+)
函数时,我立刻想到了一个折叠:总结树中的所有元素。 (请注意,使结构可折叠的规范方法是使其成为Foldable
的实例(惊喜!)并定义foldMap
或foldr
)
但是,您的家庭作业似乎是为您的树定义一个映射函数。
现在,关于你自己的问题,将一个函数转换为一个树。通过将a
,b
和c
添加到您的树中,有点不清楚是什么意思,但让我们稍微了解一下这个想法。为简单起见,我不打算做一个完全通用的功能。此外,由于你的功能"树"相当不平衡,我称之为FunHistory
而不是Tree
。这将代表功能应用程序的历史。
data FunHistory a b = Result b (FunHistory a b)
| Application (a -> FunHistory a b) a (FunHistory a b)
| Base (a -> FunHistory a b)
现在这种类型有点奇怪。 Result
包含b
类型的结果,以及指向函数应用程序的之前历史记录的链接。 Base
包含具有 no 功能应用程序历史记录的函数,以及在提供类型a
的值时生成未来历史记录的功能。那么,Application
是一个中间记录,它提供了生成未来历史的能力,以及记录过去的历史记录,以及应用于过去历史的值。
现在让我们为方便起见做一些功能。系好安全带,这可能会变得颠簸。
mkHist :: (a -> b) -> FunHistory a b
mkHist f = let h = Base (\x -> Result (f x) h) in h
给定一个单参数函数,我们可以通过...... magic创建一个历史记录。这种特殊的魔法味道被称为“懒惰”#34;和"递归让"。
让我们继续并创建一个函数,该函数将采用FunHistory
和输入值,并移动历史记录。可悲的是,这不是一个完整的功能;对Result
的{{1}}类型未定义。
FunHistory
对于单参数函数,这很好,但是对于这样的简单情况,从不需要中间-- The caller should make sure it isn't a `Result` type before using this function
app :: a -> FunHistory a b -> FunHistory a b
app x (Result _ _) = undefined
app x (Application f _ _) = f x
app x (Base f) = f x
构造函数。让我们尝试为2参数函数创建一个智能构造函数:
Application
让我们现在尝试使用3参数函数:
mkHist2 :: (a -> a -> b) -> FunHistory a b
mkHist2 f = let h = Base (\x -> mkHist' f x h)
in h
mkHist' f x h = let h' = Application (\y -> Result (f x y) h') x h
in h'
现在是一个4参数函数:
mkHist3 :: (a -> a -> a -> b) -> FunHistory a b
mkHist3 f = let h = Base (\x -> mkHist2' f x h)
in h
mkHist2' f x h = let h' = Application (\y -> mkHist' (f x) y h') x h
in h'
好好看看;这些函数看起来几乎完全像mkHist4 :: (a -> a -> a -> b) -> FunHistory a b
mkHist4 f = let h = Base (\x -> mkHist3' f x h)
in h
mkHist3' f x h = let h' = Application (\y -> mkHist2' (f x) y h') x h
in h'
和mkHist3
!从这里开始的下一步是将这些函数概括为类型类,以便它可以扩展为具有任意数量输入的函数。问题是所有输入都必须具有相同的类型。
[警告:此代码未经测试,但我确定它的大部分都是正确的...... ish]
mkHist2'