将函数映射到haskell中的树

时间:2011-12-23 08:11:07

标签: haskell tree

我们可以这样定义二叉树:

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)

是否可以像这样定义一般功能? 如果您认为我的问题毫无意义,那么请将其投票。

更多信息:我提炼了我的问题。我认为我的树是另一种说明部分功能和咖喱的方法。

2 个答案:

答案 0 :(得分:1)

你对这项任务并不了解。在树上映射函数是将函数应用于树中包含的每个数据元素。

我建议你先画一个包含数字的树。

             1
           /   \
          2     3
           \   / \
            4 6   5  

您可以使用Tree a数据类型对此树进行编码,即将树编写为包含NodeEmpty构造函数的表达式吗?

以下是一些提示:

  • 顶部节点包含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)

鉴于功能数据结构(在本例中为树),通常可以使用两种常规方法。

  1. 地图
  2. 折叠
  3. 映射是您获取函数f :: a -> b和结构origTree :: Tree a的位置,并将函数应用于结构的元素,从而生成newTree :: Tree b 。 (请注意,使结构可映射的规范方法是使其成为Functor,并定义fmap

    Folding 是您以某种方式将结构的所有元素组合成一些新值的地方。当你说你有一个Tree(+)函数时,我立刻想到了一个折叠:总结树中的所有元素。 (请注意,使结构可折叠的规范方法是使其成为Foldable的实例(惊喜!)并定义foldMapfoldr

    但是,您的家庭作业似乎是为您的树定义一个映射函数。


    现在,关于你自己的问题,将一个函数转换为一个树。通过将abc添加到您的树中,有点不清楚是什么意思,但让我们稍微了解一下这个想法。为简单起见,我不打算做一个完全通用的功能。此外,由于你的功能"树"相当不平衡,我称之为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'