while

while は、「ある条件が成り立っている間、同じ処理を繰り返す」ための構文です。while P do B という構文で用いて、条件 P が成り立つ間、命令 B を繰り返し実行します。

/-- 1から5までの数字をリストに詰めて返す -/
def packOneToFive : List Nat := Id.run do
  let mut result := []
  let mut n := 1
  while n ≤ 5 do
    result := result ++ [n]
    n := n + 1
  return result

#guard packOneToFive = [1, 2, 3, 4, 5]

while ループと停止性

while ループを使うと、停止するとは限らない計算が(停止性の証明を与えてもいないのに)書けてしまいます。たとえば以下は、述語 P : Nat → Bool を成り立たせる最小の自然数を探してくる関数の例です。

/-- 述語 `P : Nat → Bool` を成り立たせる最小の自然数を返す -/
def searchMinExample (P : Nat → Bool) : Nat := Id.run do
  let mut n := 0
  while !P n do
    n := n + 1
  return n

#guard searchMinExample (fun n => n % 7 = 1) = 1

なぜ停止性の証明が必要ないのかというと、while ループが内部で Lean.Loop.forIn という関数に展開されるのですが、これが partial で修飾された関数だからです。

したがって特に、while で定義された関数について何かを証明することはできません。たとえ明らかに停止する関数であっても、while で書かれた部分については証明不能になります。

/-- 明らかに停止するし明らかに`0`しか返さないはずの関数 -/
def trivialWhile : Nat := Id.run do
  let mut m := 0
  while false do
    m := m + 1
  return m

-- 明らかなはずなのに全然証明できない
example : trivialWhile = 0 := by
  fail_if_success rfl
  fail_if_success try?
  sorry