def
def は、関数や定数など、グローバルに項を定義するための基本的なコマンドです。
/-- 1を足す関数 -/
def addOne (n : Nat) : Nat := n + 1
-- 関数だけでなく定数も定義できる
def foo := "hello"
構文ルール
def は以下のような構文ルールを持ちます。
defの後に関数名・定数名を書きます。- 関数名の後に引数を指定します。引数なしだと定数になります。
- 引数の後に
:によって返り値の型を指定します。返り値の値が関数本体などから推論できる場合は省略できます。 :=の後に関数の本体を書きます。
なお theorem コマンドも同様の構文ルールを持ちます。
再帰関数
再帰関数も同様の構文で定義することができます。
/-- 階乗関数 -/
def factorial (n : Nat) : Nat :=
match n with
| 0 => 1
| n + 1 => (n + 1) * factorial n
#guard factorial 7 = 5040
このとき、裏で Lean は再帰関数が停止することの証明を生成しようとし、証明が失敗するとエラーになります。これについて詳しくは以下のページを参照してください。
引数の指定
関数を定義するとき、隣接する同じ型の引数に対しては : を使いまわすことができます。
def add (n m : Nat) : Nat := n + m
def threeAdd (n m l : Nat) : Nat := n + m + l
また丸括弧 () ではなくて波括弧で引数を指定すると、その引数は暗黙の引数になり、Lean が推論して埋めてくれるようになります。
/-- リストの長さを返す -/
def List.myLength {α : Type} (xs : List α) : Nat :=
match xs with
| [] => 0
| _ :: xs => 1 + myLength xs
-- `α` を引数として与えなくても呼び出すことができる
#guard List.myLength [1, 2, 3] = 3
名前付き引数
通常は引数をスペース区切りで渡しますが、それだと引数を埋める順番が固定されます。引数の名前を指定して値を渡すこともできて、そうすれば引数の順番を気にする必要はありません。これを 名前付き引数(named arguments) といいます。
/-- a に 10 を掛けてから b を足す -/
def addAndMul (a b : Int) : Int :=
10 * a + b
-- 何も指定しないと a, b の順番で引数を渡すことになる
#guard addAndMul 3 2 == 32
-- 引数名を指定して渡すと、引数を好きな順番で渡せる
#guard addAndMul (b := 3) (a := 2) == 23
引数のデフォルト値
関数の引数には、デフォルト値を設定することができます。デフォルト値を設定すると、引数を省略して呼び出したときにその値が自動的に使われます。
/-- `name` から挨拶文を生成する -/
def greetWithDefault (name : String) (punct := "!") : String :=
name ++ "さん、こんにちは" ++ punct
-- `punct` を省略しているが、デフォルトの `!` が使われている
#guard greetWithDefault "山本" == "山本さん、こんにちは!"
明示的に指定したくなった場合は、名前付き引数として渡します。
#guard greetWithDefault "山本" (punct := "!!!") == "山本さん、こんにちは!!!"