Isabelle/Isar(その18)

 更に2つ目のサブゴールは,帰納法の変数nが1以上,つまり,Suc n_の形の場合なので,場合を設定するコマンドcase Suc を用いて簡素化出来ます.それは

lemma
  fixes "a" :: "nat => real" and "n" :: "nat"
  assumes "a 0 = 0" and "ALL n. a (Suc n) = 2 * a n + 1"
  shows "a n = 2 ^ n - 1"
proof (induction n)
  show "a 0 = 2 ^ 0 - 1"
    using assms(1) by simp
next
  case Suc
  thus "?case"
    using assms(2) by simp
qed

という書式であり,case Sucは,帰納法の変数nがSuc n_の形の場合をサブゴールにするということで,これにより

  fix n_
  let ?case = "a (Suc n_) = 2 ^ Suc n_ - 1"
  assume Suc: "a n_ = 2 ^ n_ - 1"

という処理がシステム内でなされています.let ?t は見ての通り別名の設定コマンド,右辺の代わりに ?t を使えるようにするものです.また,assumeした式に Suc の名前が自動的に付くので,thusではなく,名指しで

  case Suc
  show "?case"
    using Suc assms(2) by simp

などとしても構いません.

 なお,1つ目のサブゴールの証明にも変数nが0の場合の設定case 0(具体的には,let ?case = "a 0 = 2 ^ 0 - 1")を用いて

lemma
  fixes "a" :: "nat => real" and "n" :: "nat"
  assumes "a 0 = 0" and "ALL n. a (Suc n) = 2 * a n + 1"
  shows "a n = 2 ^ n - 1"
proof (induction n)
  case 0
  show "?case"
    using assms(1) by simp
next
  case Suc
  thus "?case"
    using assms(2) by simp
qed

とすれば全体のバランスは良くなりますが,可読性という大儀が損なわれそうです.

 なお,caseコマンドの引数の0やSucは場合の名前であり,(inducton n)が参照する帰納法のルールが略されている(一般にはinduction n rule: ルール名として指定)ので,nが自然数であることより,自動的に適用されたnat_inductの属性case_nameで定められたもので,内容はprint_casesコマンドで次のように確認できます.

cases:
0:
let "?case" = "a 0 = 2 ^ 0 - 1"
Suc:
fix n_
let "?case" = "a (Suc n_) = 2 ^ Suc n_ - 1"
assume Suc.IH: "a n_ = 2 ^ n_ - 1" and Suc.prems:

ここでn_は仮の変数であり,case Suc ではなく例えばcase (Suc m)と入力すればmに置き換わり,それが任意に固定された変数となります.また,見ての通り,帰納法の仮定には,Suc.IHという名も付いているので

  case Suc
  show "?case"
    using Suc.IH assms(2) by simp

でも構いません.Suc.premsというのは,結論がP n ==> Q nの形の際に,帰納法の仮定以外に,メタの含意の前件premiseもP (Suc n_) も仮定に加えられるので,その名前です.