简体   繁体   English

如果没有我们在数据类型上定义Eq,Haskell如何进行模式匹配?

[英]How does Haskell do pattern matching without us defining an Eq on our data types?

I have defined a binary tree: 我已经定义了一个二叉树:

data Tree = Null | Node Tree Int Tree

and have implemented a function that'll yield the sum of the values of all its nodes: 并实现了一个函数,它将产生所有节点的值的总和:

sumOfValues :: Tree -> Int
sumOfValues Null = 0
sumOfValues (Node Null v Null) = v
sumOfValues (Node Null v t2) = v + (sumOfValues t2)
sumOfValues (Node t1 v Null) = v + (sumOfValues t1)
sumOfValues (Node t1 v t2) = v + (sumOfValues t1) + (sumOfValues t2)

It works as expected. 它按预期工作。 I had the idea of also trying to implement it using guards: 我有想法也尝试使用警卫来实现它:

sumOfValues2 :: Tree -> Int
sumOfValues2 Null = 0
sumOfValues2 (Node t1 v t2)
    | t1 == Null && t2 == Null  = v
    | t1 == Null            = v + (sumOfValues2 t2)
    |               t2 == Null  = v + (sumOfValues2 t1)
    |   otherwise       = v + (sumOfValues2 t1) + (sumOfValues2 t2)

but this one doesn't work because I haven't implemented Eq , I believe: 但是这个没有用,因为我没有实现Eq ,我相信:

 No instance for (Eq Tree) arising from a use of `==' at zzz3.hs:13:3-12 Possible fix: add an instance declaration for (Eq Tree) In the first argument of `(&&)', namely `t1 == Null' In the expression: t1 == Null && t2 == Null In a stmt of a pattern guard for the definition of `sumOfValues2': t1 == Null && t2 == Null 

The question that has to be made, then, is how can Haskell make pattern matching without knowing when a passed argument matches, without resorting to Eq ? 那么,必须要做的问题是Haskell如何在不知道何时传递的参数匹配的情况下进行模式匹配,而不依赖于Eq

Edit 编辑

Your arguments seem to revolve around the fact that Haskell is not indeed comparing the arguments of the function, but instead on the "form" and types of signature to know which sub-function to match. 你的论证似乎围绕着这样一个事实:Haskell确实没有比较函数的参数,而是在“形式”和签名类型上知道要匹配哪个子函数。 But how about this? 但是这个怎么样?

f :: Int -> Int -> Int
f 1 _ = 666
f 2 _ = 777
f _ 1 = 888
f _ _ = 999

When running f 2 9 , won't it have to use Eq to know which one of the subfunctions is the right one? 运行f 2 9 ,是否必须使用Eq才能知道哪一个子功能是正确的? All of them are equal (contrary to my initial Tree example when we had Tree/Node/Null). 所有这些都是相同的(与我的Tree / Node / Null时的初始Tree示例相反)。 Or is the actual definition of an Int something like 或者是Int的实际定义

data Int = -2^32 | -109212 ... | 0 | ... +2^32 

?

For pattern matching, Haskell uses the structure of the value and the constructors used. 对于模式匹配,Haskell使用值的结构和使用的构造函数。 For example, if you evaluate 例如,如果您评估

sumOfValues (Node Null 5 (Node Null 10 Null))

it checks the patterns from top to bottom: 它从上到下检查模式:

  • the first one, Null , doesn't match because it has a different structure 第一个, Null ,不匹配,因为它有不同的结构

  • the second one, (Node Null v Null) , doesn't match because the last component, Null , has a different structure than (Node Null 10 Null) (pattern matching proceeds recursively) 第二个(Node Null v Null)不匹配,因为最后一个组件Null具有与(Node Null 10 Null)不同的结构(模式匹配递归地进行)

  • the third one matches with v bound to 5 and t2 bound to (Node Null 10 Null) . 第三个匹配v绑定到5和t2绑定到(Node Null 10 Null)

Eq and the == operator it defines are an unrelated mechanism; Eq和它定义的==运算符是一个不相关的机制; making Tree an instance of Eq won't change how pattern matching works. 使Tree成为Eq的实例不会改变模式匹配的工作方式。

I think your thinking here is a bit backward: Pattern matching is the most basic way of using a value in Haskell; 我认为你的想法有点落后:模式匹配是在Haskell中使用值的最基本方式; except for some basic types like Int , Eq is implemented using pattern matching, not the other way around. 除了像Int类的一些基本类型, Eq是使用模式匹配实现的,而不是相反。

Pattern matching and numbers 模式匹配和数字

It turns out numeric literals are a special case. 事实证明,数字文字是一种特殊情况。 According to the Haskell report ( link ): 根据Haskell报告( 链接 ):

Matching a numeric, character, or string literal pattern k against a value v succeeds if v == k , where == is overloaded based on the type of the pattern. 如果v == k ,则将数字,字符或字符串文字模式k与值v匹配成功,其中==基于模式的类型重载。

Haskell知道使用什么类型的构造函数来构造特定的实例,这就是成功模式匹配所需的全部内容。

The thing you're missing is that you're assuming Null is some constant value like in C or Java. 你缺少的是你假设Null是一些像C或Java那样的常量值。 It's not — it's a constructor for the Tree type. 它不是 - 它是Tree类型的构造函数。

Pattern-matches do construction in reverse. 模式匹配反向构建。 Instead of you providing two values to the constructor and it gives you a value of the appropriate type, you provide a value of the type to the constructor and it gives you the constituent values that make up the type. 您不是向构造函数提供两个值,而是为您提供适当类型的值,而是为构造函数提供类型的值,并为您提供构成该类型的组成值。 The language knows which branch of the discriminated union was used to construct the value, so it can match against the correct pattern. 语言知道区分联合的哪个分支用于构造值,因此它可以匹配正确的模式。 Thus, there is no need for equality because it's all just constructors and variables — no actual values are being compared (or even necessarily evaluated). 因此,不需要相等,因为它只是构造函数和变量 - 没有实际值进行比较(或者甚至必须进行评估)。

With regards to your edit about Ints: 关于你对Ints的编辑:

Yes, Int is basically a type with an obscenely large number of constructors along the lines of -2 | -1 | 0 | 1 | 2 | 3 | 4 … 是的,Int基本上是一个类型,在-2 | -1 | 0 | 1 | 2 | 3 | 4 …的行上有大量的构造函数 -2 | -1 | 0 | 1 | 2 | 3 | 4 … -2 | -1 | 0 | 1 | 2 | 3 | 4 … . -2 | -1 | 0 | 1 | 2 | 3 | 4 … It's a bit handwavy, but it works in practice. 它有点手工,但它在实践中有效。

At some point you need pattern matching when you don't want to use Eq. 某些时候,当您不想使用Eq时,您需要模式匹配。 Eg you could define 你可以定义

isEmpty :: Tree -> Bool
isEmpty Null = True
isEmpty _ = False

sumOfValues2 :: Tree -> Int
sumOfValues2 Null = 0
sumOfValues2 (Node t1 v t2)
    | isEmpty t1 && isEmpty t2 = v
    | isEmpty t1 = v + (sumOfValues2 t2)
    | isEmpty t2 = v + (sumOfValues2 t1)
    | otherwise = v + (sumOfValues2 t1) + (sumOfValues2 t2)

BTW, you don't need all that cases, just this: 顺便说一下,你不需要所有这些情况,只需这样:

sumOfValues :: Tree -> Int
sumOfValues Null = 0
sumOfValues (Node t1 v t2) = v + (sumOfValues t1) + (sumOfValues t2)

You can think of the data constructors as tags. 您可以将数据构造函数视为标记。 To do a pattern matching on a value of Tree , the compiled code extracts the tag and just knows which branch to dispatch to. 要对Tree的值进行模式匹配,编译后的代码将提取标记,并且只知道要分派到哪个分支。 It doesn't care about the real value, so you don't need Eq . 它并不关心真正的价值,所以你不需要Eq

Pattern matching depends on the syntax. 模式匹配取决于语法。 Eg if you write a pattern involving constructors, pattern matching by constructors is what you'll get. 例如,如果你编写一个涉及构造函数的模式,那么构造函数的模式匹配就是你得到的。 If you write a pattern involving litteral expressions (and litteral floating point values or integers are just that) you get an equality test (from the Eq class) using whatever overloaded definition of the (==) operator your type may provide. 如果你编写一个涉及litteral表达式的模式(并且litteral浮点值或整数就是这样),你可以使用你的类型可能提供的(==)运算符的任何重载定义来获得一个相等的测试(来自Eq类)。

So if you overload your Fred data type which is of class Num and has a constructor, also called Fred you could define equality against Integers by checking if the given Integer is 1. Now in that weird scenario, unless I'm overlooking something, the following ought to work: 因此,如果你重载你的Fred数据类型,它是类Num并且有一个构造函数,也称为Fred你可以通过检查给定的Integer是否来定义与Integers的相等性。现在在那个奇怪的场景中,除非我忽略了某些东西,以下应该工作:

getFred :: Fred -- get a Fred
getFred = Fred -- invoke the Fred constructor

testFred :: Fred -> Bool -- tests if Fred's equality operator considers 1 equal to Fred
testFred 1 = True -- overloaded (==) tests against 1, so should be true
testFred _ = False -- shouldn't happen, then...

isFredOne = testFred $ getFred -- returns True

I justify this type of thing to myself by saying that pattern matching is core to what Haskell is - the Eq typeclass is not. 我通过说模式匹配是Haskell的核心来证明这种类型的东西--Eq类型类不是。

So while there are features similar to pattern matching which require Eq, they are not pattern matching and could be implemented on top of pattern matching. 因此虽然存在类似于需要Eq的模式匹配的特征,但它们不是模式匹配并且可以在模式匹配之上实现。

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

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