congr

f₁ = f₂ かつ a₁ = a₂ ならば f₁ a₁ = f₂ a₂」という事実は、等号が合同(congruence)関係であると解釈できますが、congr はこれを使ってゴールを分解するタクティクです。

congr は、⊢ f as = f bs という形のゴールがあったときに、ゴールを ⊢ as = bs に変えます。再帰的に適用されるので、⊢ g (f as) = g (f bs) という形のゴールでも ⊢ as = bs というゴールになります。

variable (X : Type) (x : Int) (f : Int → Int)

example (h : x = 0) : f (2 + x) = f 2 := by
  congr
  show 2 + x = 2

  simp [h]

congr は等号以外の関係は扱えません。等号以外の関係の合同性も扱えるタクティクに gcongr があります。

variable (a b : Int)

example (h : a = b) : a + 1 = b + 1 := by
  -- 等号の場合はOK
  congr

example (h : a < b) : a + 1 < b + 1 := by
  -- 不等号の場合エラーにはならないが何も起こらない
  congr
  show a + 1 < b + 1

  exact Int.add_lt_add_right h 1

再帰の深さの調節

congr が適用される再帰の深さを引数として渡すことができます。これは、主に単に congr とするだけだと「行き過ぎ」になるときに調整する目的で使用されます。

example (g : Int → X) (h : x = 0) (hf : ∀ x, f x = f (- x)) :
    g (f (2 + x)) = g (f (- 2)) := by

  -- congr の再帰がアグレッシブすぎて上手くいかないことがある
  try
    congr

    -- 分解しすぎた
    show 2 + x = -2

    -- これでは示すことができない
    fail

  -- 再帰の深さを数値として指定できる
  congr 1

  -- ちょうどよい分解になった
  show f (2 + x) = f (-2)

  simp only [h, Nat.add_zero]
  exact hf _