1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use crate::expr::{self, Expr, Identifier};

/// 定義済み関数を表現する
///
/// 関数とラムダ抽象はよく似ているが、関数が 0 以上の arity を持つ点で異なる
#[derive(Debug, Clone, PartialEq)]
pub struct Func {
    name: Identifier,
    params: Vec<Identifier>,
    body: Expr,
}

impl Func {
    pub fn name(&self) -> &str {
        self.name.as_str()
    }

    pub fn params(&self) -> &Vec<Identifier> {
        &self.params
    }

    pub fn body(&self) -> &Expr {
        &self.body
    }

    /// 関数の引数の個数
    ///
    /// 0 以上の整数値を返す
    pub fn arity(&self) -> usize {
        self.params.len()
    }

    /// 関数に引数を与え評価した結果を返す
    pub fn apply(&self, args: Vec<Expr>) -> Expr {
        let mut body = self.body.clone();
        for (param, arg) in self.params.iter().zip(args) {
            body.substitute(param, &arg);
        }
        body
    }
}

pub fn new<Name, Param, Body>(name: Name, params: Vec<Param>, body: Body) -> Func
where
    Name: Into<Identifier>,
    Param: Into<Identifier>,
    Body: Into<Expr>,
{
    Func {
        name: name.into(),
        params: params.into_iter().map(|i| i.into()).collect(),
        body: body.into(),
    }
}

impl From<Func> for Expr {
    fn from(f: Func) -> Expr {
        let mut expr = f.body;
        for param in f.params.into_iter().rev() {
            expr = expr::l(param, expr);
        }
        expr
    }
}

// ========================================================================== //

#[cfg(test)]
mod tests {
    use super::*;
    use crate::expr;

    #[test]
    fn test_name() {
        let f = new("i", vec!["x"], "x");
        assert_eq!(f.name(), "i");
    }

    #[test]
    fn test_params() {
        let f = new("k", vec!["x", "y"], "x");
        assert_eq!(f.params(), &vec!["x".into(), "y".into()]);
    }

    #[test]
    fn test_body() {
        let f = new(
            "s",
            vec!["x", "y", "z"],
            expr::a(expr::a("x", "z"), expr::a("y", "z")),
        );
        assert_eq!(f.body(), &expr::a(expr::a("x", "z"), expr::a("y", "z")));
    }

    #[test]
    fn test_arity() {
        let f = new("i", vec!["x"], "x");
        assert_eq!(f.arity(), 1);

        let f = new("k", vec!["x", "y"], "x");
        assert_eq!(f.arity(), 2);

        let f = new(
            "s",
            vec!["x", "y", "z"],
            expr::a(expr::a("x", "z"), expr::a("y", "z")),
        );
        assert_eq!(f.arity(), 3);
    }

    #[test]
    fn test_apply() {
        let f = new("i", vec!["x"], "x");
        assert_eq!(f.apply(vec![expr::v("a")]), expr::v("a"));

        let f = new("k", vec!["x", "y"], "x");
        assert_eq!(f.apply(vec![expr::v("a"), expr::v("b")]), expr::v("a"));

        let f = new(
            "s",
            vec!["x", "y", "z"],
            expr::a(expr::a("x", "z"), expr::a("y", "z")),
        );
        assert_eq!(
            f.apply(vec![expr::v("a"), expr::v("b"), expr::v("c")]),
            expr::a(expr::a("a", "c"), expr::a("b", "c"))
        );

        let f = new("XX", vec!["x"], expr::a("x", "x"));
        assert_eq!(f.apply(vec![expr::v("a")]), expr::a("a", "a"));
    }
}