演算式用の簡単な再帰エバリュエータを作成してみましょう。
An example of a small arithmetic expression could be 10 + 20
, which evaluates to 30
. We can represent the expression as a tree:
A bigger and more complex expression would be (10 * 9) + ((3 - 4) * 5)
, which evaluate to 85
. We represent this as a much bigger tree:
In code, we will represent the tree with two types:
#![allow(unused)]
fn main() {
#[derive(Debug)]
enum Operation {
Add,
Sub,
Mul,
Div,
}
#[derive(Debug)]
enum Expression {
Op { op: Operation, left: Box<Expression>, right: Box<Expression> },
Value(i64),
}
}
ここでの Box
型はスマート ポインタです。詳細はこの講座で後ほど説明します。テストで見られるように、式は Box::new
で「ボックス化」できます。ボックス化された式を評価するには、逆参照演算子(*
)を使用して「ボックス化解除」します(eval(*boxed_expr)
)。
一部の式は評価できず、エラーが返されます。標準の Result<Value, String>
型は、成功した値(Ok(Value)
)またはエラー(Err(String)
)のいずれかを表す列挙型です。この型については、後ほど詳しく説明します。
コードをコピーして Rust プレイグラウンドに貼り付け、eval
の実装を開始します。完成したエバリュエータはテストに合格する必要があります。todo!()
を使用して、テストを 1 つずつ実施することをおすすめします。#[ignore]
を使用して、テストを一時的にスキップすることもできます。
#[test]
#[ignore]
fn test_value() { .. }
#![allow(unused)]
fn main() {
#[derive(Debug)]
enum Operation {
Add,
Sub,
Mul,
Div,
}
#[derive(Debug)]
enum Expression {
Op { op: Operation, left: Box<Expression>, right: Box<Expression> },
Value(i64),
}
fn eval(e: Expression) -> Result<i64, String> {
todo!()
}
#[test]
fn test_value() {
assert_eq!(eval(Expression::Value(19)), Ok(19));
}
#[test]
fn test_sum() {
assert_eq!(
eval(Expression::Op {
op: Operation::Add,
left: Box::new(Expression::Value(10)),
right: Box::new(Expression::Value(20)),
}),
Ok(30)
);
}
#[test]
fn test_recursion() {
let term1 = Expression::Op {
op: Operation::Mul,
left: Box::new(Expression::Value(10)),
right: Box::new(Expression::Value(9)),
};
let term2 = Expression::Op {
op: Operation::Mul,
left: Box::new(Expression::Op {
op: Operation::Sub,
left: Box::new(Expression::Value(3)),
right: Box::new(Expression::Value(4)),
}),
right: Box::new(Expression::Value(5)),
};
assert_eq!(
eval(Expression::Op {
op: Operation::Add,
left: Box::new(term1),
right: Box::new(term2),
}),
Ok(85)
);
}
#[test]
fn test_zeros() {
assert_eq!(
eval(Expression::Op {
op: Operation::Add,
left: Box::new(Expression::Value(0)),
right: Box::new(Expression::Value(0))
}),
Ok(0)
);
assert_eq!(
eval(Expression::Op {
op: Operation::Mul,
left: Box::new(Expression::Value(0)),
right: Box::new(Expression::Value(0))
}),
Ok(0)
);
assert_eq!(
eval(Expression::Op {
op: Operation::Sub,
left: Box::new(Expression::Value(0)),
right: Box::new(Expression::Value(0))
}),
Ok(0)
);
}
#[test]
fn test_error() {
assert_eq!(
eval(Expression::Op {
op: Operation::Div,
left: Box::new(Expression::Value(99)),
right: Box::new(Expression::Value(0)),
}),
Err(String::from("division by zero"))
);
}
}