简体   繁体   English

Haskell使用数据类型

[英]Haskell usage of data type

I am reading making our own types and typeclasses in learn you a haskell. 我正在阅读制作我们自己的类型和类型类来学习你的哈克尔。

In section Algebraic data types intro I notice: 代数数据类型介绍中我注意到:

data Point = Point Float Float deriving (Show)  
data Shape = Circle Point Float | Rectangle Point Point deriving (Show)

surface :: Shape -> Float
surface (Rectangle (Point x1 y1) (Point x2 y2)) = abs (x2 - x1) * abs (y2 - y1) 

In surface (Rectangle (Point x1 y1) (Point x2 y2)) , we indicate the parameters for Rectangle are of type Point. surface (Rectangle (Point x1 y1) (Point x2 y2)) ,我们指出Rectangle的参数是Point类型。

However, in section Recursive data structures : 但是,在递归数据结构部分中:

data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)
singleton :: a -> Tree a  
singleton x = Node x EmptyTree EmptyTree  

treeInsert :: (Ord a) => a -> Tree a -> Tree a  
treeInsert x EmptyTree = singleton x  
treeInsert x (Node a left right)   
    | x == a = Node x left right  
    | x < a  = Node a (treeInsert x left) right  
    | x > a  = Node a left (treeInsert x right)

We don't indicate left 's and right 's data types are Tree a in treeInsert x (Node a left right) . 我们没有指出leftright的数据类型是treeInsert x (Node a left right)中的Tree a treeInsert x (Node a left right) How does the compiler know their type? 编译器如何知道它们的类型?

I think you have a misconception here: 我觉得你有一个误解:

In surface (Rectangle (Point x1 y1) (Point x2 y2)) , we indicate the parameters for Rectangle are of type Point . surface (Rectangle (Point x1 y1) (Point x2 y2)) ,我们指出Rectangle的参数是Point类型。

This does indicate that the parameters are of type point, but perhaps not in the way that you think. 这确实表明参数属于point类型,但可能与您的想法不同。 The " Point " in Point x1 y1 is not a type -- it's a constructor which happens to be named the same way as the type it constructs. Point x1 y1的“ Point ”不是一个类型 - 它是一个构造函数,其命名方式与它构造的类型相同。 If we declared Point as 如果我们宣布Point

data Point = MakePoint Float Float

then you would say 然后你会说

surface (Rectangle (MakePoint x1 y1) (MakePoint x2 y2)) 

For clarity, I will continue using MakePoint for the constructor and Point for the type. 为清楚起见,我将继续使用MakePoint作为构造函数,使用Point作为类型。 It is legal Haskell to name these the same because the compiler can always tell from context, but humans sometimes have more trouble. 合法的Haskell将其命名为相同,因为编译器总是可以从上下文中判断,但人类有时会遇到更多麻烦。

Within the context 在上下文中

surface (Rectangle (MakePoint x1 y1) (MakePoint x2 y2)) = ...

we know that the subexpression MakePoint x1 y1 has type Point from two different places. 我们知道子表达式MakePoint x1 y1具有来自两个不同位置的Point类型。 One is that the constructor Rectangle has type 一个是构造函数Rectangle具有类型

Rectangle :: Point -> Point -> Shape

so we know that both of its arguments must be points (this is outside-in type inference, where we get the type of something from the context in which it's used); 所以我们知道,无论它的参数必须是点(这是由外向内的类型推断,我们得到的来自其中它的使用范围内的事情型); and the other is that the constructor MakePoint has type 另一个是构造函数MakePoint有类型

MakePoint :: Float -> Float -> Point

so we know that MakePoint x1 y1 represents a value of type Point (this is inside-out type inference, where we get the type of an expression from its components). 所以我们知道MakePoint x1 y1表示Point类型的值(这是由里而外的类型推断,我们从其组件中获取表达式的类型)。 The compiler, in a way, uses both of these approaches and makes sure that they match. 在某种程度上,编译器使用这两种方法并确保它们匹配。

However, sometimes one or the other of these kinds of information is lacking, for example x1 in our example. 但是,有时缺少这些信息中的一种或另一种,例如我们的例子中的x1 We have no inside-out information about x1 (well, we would if we looked at the right hand side of the equation, which the compiler also does, but let's ignore that for now), all we have is that the arguments to the MakePoint constructor must be Float s, so we know that x1 must be a Float . 我们没有关于x1内部信息(好吧,如果我们查看方程式的右侧,编译器也MakePoint ,但我们现在忽略它),我们所拥有的只是MakePoint的参数构造函数必须是Float ,所以我们知道x1必须是Float This is valid and inferred by the compiler; 这是编译器有效和推断的; there is no need to state it explicitly. 没有必要明确说明。

In the Tree example there is more confusing naming going on (which, once you get this, ceases to be confusing and begins being helpful, but it's good to draw a clear distinction at the start), so I'm going to rename the first argument of Node to from a to v : Tree例子中,更令人困惑的命名正在进行(一旦你得到它,不再混淆并开始有用,但最好在开始时画出明显的区别),所以我要重命名第一个Nodeav

treeInsert :: (Ord a) => a -> Tree a -> Tree a  
treeInsert x EmptyTree = singleton x  
treeInsert x (Node v left right)   
    | x == v = Node x left right  
    | x < v  = Node v (treeInsert x left) right  
    | x > v  = Node v left (treeInsert x right)

The same thing is happening with left and right as was with x1 above: there is no inside-out structure to use, but we know that the Node constructor takes an a and two Tree a s, so v must be of type a , and left and right must be of type Tree a . 同样的事情与发生leftright这一点与x1上面:没有由内而外的结构使用,但我们知道, Node构造函数采用a和两个Tree a S,所以v的类型必须a ,和leftright 必须Tree a类型。 The compiler deduces this from the context. 编译器从上下文中推断出这一点。

In surface (Rectangle (Point x1 y1) (Point x2 y2)) , we indicate the parameters for Rectangle are of type Point. 在surface (Rectangle (Point x1 y1) (Point x2 y2)) ,我们指出Rectangle的参数是Point类型。

No. This is your misconception. 不,这是你的错误观念。 The compiler knows the type of Rectangle 's arguments because of the data declaration: 由于数据声明,编译器知道Rectangle参数的类型:

data ... | Rectangle Point Point

In the code you were referencing: 在您引用的代码中:

surface (Rectangle (Point x1 y1) (Point x2 y2))

That is called a pattern match. 这称为模式匹配。 Surface takes a rectangle, which we pattern match in order to bind variable names to the parameters. Surface采用矩形,我们将模式匹配,以便将变量名称绑定到参数。 We also pattern match on each parameter to gain access to the sub-parameters and bind the variable names x1 , y1 , x2 , and y2 . 我们还对每个参数进行模式匹配以获得对子参数的访问并绑定变量名称x1y1x2y2

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

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