【问题标题】:How to use the fold function in Haskell with other datatypes如何将 Haskell 中的 fold 函数与其他数据类型一起使用
【发布时间】:2019-06-21 05:56:27
【问题描述】:

对于如何为新数据类型创建折叠函数是否有一种通用的思维方式?

例如数据树的折叠函数为:

data Tree t = Leaf | Node t (Tree t) (Tree t)
              deriving (Eq,Ord,Show)

treeFold:: (a -> b -> b -> b) -> b -> Tree a -> b
treeFold f e Leaf = e
treeFold f e (Node x l r) = f x (treeFold f e l) (treeFold f e r)

例如,我将如何为以下数据创建折叠函数?

data Json a = Val a | Obj [(String, Json a)]

我知道该类型必须包含 2 个函数,一个用于 Val 和 Obj 的情况。创建折叠时我必须考虑什么?我希望我的问题是有道理的。我刚刚遇到许多不同的数据类型,要求为数据类型编写折叠函数,但我似乎没有找到模式。

【问题讨论】:

    标签: haskell fold algebraic-data-types


    【解决方案1】:

    正如 Willem Van Onsem 在(现已删除的)评论中指出的那样,您尝试实现的也称为 catamorphism。我在Does each type have a unique catamorphism? 上写了一些关于我想你可能称之为初学者的变质观点的文章。您可以非常机械地推导出类型的变质(或证明不存在)。如果您的类型有 N 个构造函数,则 fold 函数必须采用 N+1 个参数:您的类型的一个值,每个构造函数一个函数。每个这样的函数都接受其对应构造函数具有的每个字段的一个参数(或者,如果构造函数没有字段,则它接受一个普通值,您可以将其想象为 0 元函数),并返回任何类型的变态值返回。

    文字比较复杂,所以我从上面链接的答案中复制相关代码,作为示例:

    data X a b f = A Int b
                 | B
                 | C (f a) (X a b f)
                 | D a
    
    xCata :: (Int -> b -> r)
          -> r
          -> (f a -> r -> r)
          -> (a -> r)
          -> X a b f
          -> r
    xCata a b c d v = case v of
      A i x -> a i x
      B -> b
      C f x -> c f (xCata a b c d x)
      D x -> d x
    

    请注意,每个函数(a、b、c、d)在关联的构造函数中的每个字段都有一个参数。在大多数情况下,您只需使用构造函数的每个字段调用函数……但是 C 的情况是怎么回事?为什么我们不写c f x 而不是c f (xCata a b c d x)?这就是递归发生的地方:cata 的工作是递归遍历(折叠)由 ADT 表示的整个树,将每个 X a b f 值转换为 r 类型的结果。令人高兴的是,只有一种可能的方法来进行这种转换:调用 xCata 时使用与您开始时传递的相同函数集。

    【讨论】:

      【解决方案2】:

      一般准则(对于在 ADT 上运行的所有函数,而不仅仅是折叠)将是“每个构造函数一个方程”:

      data MyType = Constructor1 Int | Constructor2 Float | Constructor3
      
      myFunc :: MyType -> Int
      myFunc (Constructor1 x) = ...
      myFunc (Constructor2 y) = ...
      myFunc Constructor3     = ...
      

      此外,实现折叠函数的最正确方法是为您的类型声明Foldable 的实例。

      【讨论】:

      • 不,Foldable 与 catamorphism 不同。尝试为您的MyType 实现可折叠:这是不可能的。
      • 我知道每个构造函数需要一个方程,我的问题更多是如何为构造函数找到正确的方程
      • 我知道,我知道(无类型参数)——它的目的是作为一个示例,说明如何将问题分解为更简单的部分(每个构造函数一个方程),而不是如何专门编写折叠。 ..
      • 不仅没有类型参数:Foldable 必须让您访问类型中a 类型的值列表,而不管它们在结构中的什么位置是。对于变质现象,您需要了解结构的能力。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-10
      • 1970-01-01
      • 1970-01-01
      • 2013-08-14
      • 2022-12-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多