我最近在大学学习了Haskell,我正在通过一系列的练习,这里有一段我无法理解的片段:
"对于简单的前缀计算器语言,请考虑以下语法:"
num ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
int ::= num | num int
expr ::= int | - expr | + expr expr | * expr expr
我很困惑如何将其转换为Haskell语法(我是Haskell和函数式编程的完全初学者,请温柔)
我怀疑num
,int
和expr
是所有可以使用data
或type
声明的类型/值,并且他们认为对计算器施加约束。但是我无法理解:如何为固定值声明type
或data
(不是变量),即0-9?另外,如何在声明中添加-
或+
等符号?
答案 0 :(得分:7)
不要在代表它的AST的语法中混淆一个字符串。比较字符串
"+ + 3 4 5"
这是你用
给出的语法中的一个字符串Plus (Plus (Literal 3) (Literal 4)) (Literal 5)
这将是String
可以解析为AST的合理的Haskell值。
答案 1 :(得分:4)
如何为固定值声明类型或数据(不是变量),即0-9?
您可以定义类型,例如
data Digit = Zero | One | Two | Three | Four | Five | Six | Seven | Eight | Nine deriving (Eq, Show)
这表示问题中的num
。显然我们不能使用0, 1, 2, 3, ...
,因为它们已被解释为Haskell中的数字。
然后,您可以定义
data Number = Single Digit | Many Digit Number deriving (Eq, Show)
相当于问题中的int
。此类型表示一个(Single ...
)或更多(Many ...
)个数字,它们一起构成一个十进制数字。例如,对于这些数据类型,数字361将为Many Three (Many Six (Single One))
。
另外,如何在声明中添加像 - 或+这样的符号?
无法将这些符号放在类型或数据声明中。但是,您可以使用操作的名称,例如Sum
,Sub
和Mul
。然后问题语法的expr
将转换为
data Expr = Lit Number
| Sub Expr Expr
| Sum Expr Expr
| Mul Expr Expr
deriving (Eq, Show)
如果我们有一个字符串"+ (- (2 5) (1 3)) 3"
,它表示问题的前缀计算器语言中的表达式,它将被解析为Sum (Sub (Lit (Many Two (Single Five))) (Lit (Many One (Single Three)))) (Single Three)
。
答案 2 :(得分:1)
如果它只是关于建模数据的练习(没有代码),答案包括在语法中添加构造函数名称(以及将文字编号更改为名称)。像
这样的东西data Num = Zero | One | Two | Three | Four | Five
| Six | Seven | Eight | Nine
data Int = Single Num | Multiple Num Int
data Exp = ExpInt Int | ExpMinus Exp Exp | ExpMul Exp Exp
| ExpMul Exp Exp
由此,您可以编写所有类型的代码,以解析和计算表达式。
答案 3 :(得分:0)
多年前,我很聪明,我宣称我的AST类型为instance
Num
,Eq
和Ord
,然后定义AST的数学和比较运算符表达式,以便expr1 + expr2
产生有效的AST。使用sevenj的声明,这将写成(+) x y = Sum x y
,其中右侧是AST表达式的构造函数。为简洁起见,one = Lit One
和two = Lit Two
。然后你可以编写one + one == two
并且运算符将使用正确的优先级生成AST。在那个和let { ... } in ...
语法的滥用允许任意缩进之间,我有办法写AST几乎只是玩具命令式语言本身,上面,下面和左边有一些样板。