简体   繁体   English

如何在Haskell中使用fold函数和其他数据类型

[英]How to use the fold function in Haskell with other datatypes

Is there an universal way of thinking on how to create a fold function for a new data type? 是否存在一种关于如何为新数据类型创建折叠函数的通用思维方式?

For example, the fold function for the data Tree is: 例如,数据树的折叠函数是:

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)

For example, how would I have to create the fold function for the following data? 例如,我如何为以下数据创建折叠函数?

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

I know the type would have to contain 2 functions, one for each ot the cases Val and Obj. 我知道类型必须包含2个函数,每个函数对应一个Val和Obj。 What do I have to consider while creating the fold? 在创建折叠时我需要考虑什么? I hope my question makes sense. 我希望我的问题有道理。 I've just came across many different datatypes where it was asked to write a fold function for a data type, and I don't seem to find the pattern. 我刚刚遇到了许多不同的数据类型,它被要求为数据类型编写折叠函数,我似乎没有找到该模式。

As Willem Van Onsem pointed out in a (now-deleted) comment, what you are trying to implement is also called a catamorphism . 正如Willem Van Onsem在一篇(现已删除的)评论中指出的那样,你试图实现的内容也被称为catamorphism I've written some about what I suppose you might call a beginner's view of catamorphisms, at Does each type have a unique catamorphism? 我已经写了一些关于我认为你可能称之为初学者对于catamorphisms的观点,在每种类型都有一个独特的变形吗? . You can derive the catamorphism for a type (or show that none can exist) quite mechanically. 你可以非常机械地推导出一种类型(或显示没有任何一种可能存在)的变形。 If your type has N constructors, the fold function must take N+1 arguments: one value of your type, and one function for each constructor. 如果您的类型具有N个构造函数,则fold函数必须采用N + 1个参数:您的类型的一个值,以及每个构造函数的一个函数。 Each such function takes one argument per field that its corresponding constructor has (or, if the constructor has no fields, it takes an ordinary value, which you can imagine as a 0-ary function), and returns a value of whatever type the catamorphism returns. 每个这样的函数对应的构造函数每个字段都有一个参数(或者,如果构造函数没有字段,它需要一个普通值,你可以想象它是一个0元函数),并返回一个任何类型的变形函数的值。回报。

It's complicated in words, so I'll copy the relevant code from the answer I linked above, as an exemplar: 它的单词很复杂,所以我将从上面链接的答案中复制相关代码,作为示例:

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

Observe that each of the functions (a, b, c, d) has one argument per field in the associated constructor. 观察到每个函数(a,b,c,d)在关联的构造函数中每个字段都有一个参数。 In most of the cases, you simply call the function with each of the constructor's fields...but what's up with the C case? 在大多数情况下,您只需使用每个构造函数的字段调用该函数......但是C的情况是什么? Why don't we write cfx instead of cf (xCata abcdx) ? 为什么我们不写cfx而不是cf (xCata abcdx) This is where the recursion happens: cata 's job is to recursively traverse (fold) the entire tree represented by your ADT, turning each X abf value into a result of type r . 这就是递归发生的地方: cata的工作是以递归方式遍历(折叠)由ADT表示的整个树,将每个X abf值转换为r类型的结果。 Happily, there's only one possible way to do that transformation: call xCata with the same set of functions you were passed to begin with. 令人高兴的是,只有一种可能的方法来进行转换:使用您开始传递的相同功能集调用xCata

The general guideline (for all functions that operate over an ADT, not just fold) would be "one equation per constructor": 一般准则(对于在ADT上运行的所有函数,而不仅仅是折叠)将是“每个构造函数的一个等式”:

data MyType = Constructor1 Int | Constructor2 Float | Constructor3

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

Also, the most proper way of implementing a fold function would be to declare an instance of Foldable for your type. 此外,实现折叠函数的最恰当方式是为您的类型声明一个Foldable实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM