11. 변환 택틱 모드
택틱 블록 안에서는 conv 키워드를 사용하여 변환 모드에 진입할 수 있습니다. 이 모드를 사용하면 가정과 목표 내부, 심지어 함수 추상화와 의존 화살표 내부까지 이동하여 재작성이나 단순화 단계를 적용할 수 있습니다.
11.1. 기본 탐색 및 재작성
첫 번째 예시로, (a b c : Nat) : a * (b * c) = a * (c * b)라는 예제를 증명해 봅시다(이 파일의 예제들은 다른 택틱으로도 즉시 끝낼 수 있기 때문에 다소 인위적입니다). 소박한 첫 번째 시도는 택틱 모드로 들어가서 rw [Nat.mul_comm]를 시도하는 것입니다. 하지만 이는 항(term)에 나타나는 맨 처음 곱셈을 교환한 뒤, 목표를 b * c * a = a * (c * b)로 변환해 버립니다. 이 문제를 해결하는 방법은 여러 가지가 있는데, 그중 하나는 더 정밀한 도구인 변환 모드(conversion mode)를 사용하는 것입니다. 다음 코드 블록은 각 줄 이후의 현재 목표를 보여줍니다.
#guard_msgs (drop all) in
example (a b c : Nat) : a * (b * c) = a * (c * b) := a:Natb:Natc:Nat⊢ a * (b * c) = a * (c * b)
a:Natb:Natc:Nat⊢ b * c * a = a * (c * b)
example (a b c : Nat) : a * (b * c) = a * (c * b) := by a:Natb:Natc:Nat⊢ a * (b * c) = a * (c * b)
conv => a:Natb:Natc:Nat| a * (b * c) = a * (c * b)
lhs a:Natb:Natc:Nat| a * (b * c)
congr a a:Natb:Natc:Nat| aa a:Natb:Natc:Nat| b * c
rfl a a:Natb:Natc:Nat| b * c
rw [Nat.mul_comm] a a:Natb:Natc:Nat| c * b
위 코드 조각은 세 가지 탐색 명령을 보여줍니다.
-
lhs는 관계(이 경우에는 동치)의 좌변으로 이동합니다. 우변으로 이동하는rhs도 있습니다. -
congr는 현재 헤드 함수(여기서는 헤드 함수가 곱셈)의 (비의존적이고 명시적인) 인자 수만큼 목표를 생성합니다. -
rfl은 반사성을 이용해 목표를 닫습니다.
해당 대상에 도달하고 나면, 일반 택틱 모드에서와 마찬가지로 rw를 사용할 수 있습니다.
변환 모드를 사용하는 두 번째 주요 이유는 바인더 아래에서 다시 쓰기 위함입니다. (fun x : Nat => 0 + x) = (fun x => x)라는 예제를 증명하고자 한다고 가정합시다. 순진한 첫 시도는 택틱 모드에 진입해 rw [Nat.zero_add]를 시도하는 것입니다. 하지만 이는 다음과 같은 당혹스러운
error: tactic 'rewrite' failed, did not find instance of the pattern
in the target expression
0 + ?n
⊢ (fun x => 0 + x) = fun x => x
해법은 다음과 같습니다:
example : (fun x : Nat => 0 + x) = (fun x => x) := by ⊢ (fun x => 0 + x) = fun x => x
conv => | (fun x => 0 + x) = fun x => x
lhs | fun x => 0 + x
intro x x:Nat| 0 + x
rw [Nat.zero_add] x:Nat| x
여기서 intro x는 fun 바인더 내부로 진입하는 탐색 명령입니다. 이 예제는 다소 인위적이며, 다음과 같이 할 수도 있다는 점에 유의하십시오.
example : (fun x : Nat => 0 + x) = (fun x => x) := by ⊢ (fun x => 0 + x) = fun x => x
funext x x:Nat⊢ 0 + x = x; rw [Nat.zero_add x:Nat⊢ x = x] All goals completed! 🐙
또는 그저
example : (fun x : Nat => 0 + x) = (fun x => x) := by ⊢ (fun x => 0 + x) = fun x => x
simp All goals completed! 🐙
conv는 conv at h를 사용하여 로컬 컨텍스트에 있는 가설 h를 다시 쓸 수도 있습니다.
11.2. 패턴 매칭
위의 명령들을 사용하여 이동하는 것은 지루할 수 있습니다. 다음과 같이 패턴 매칭을 사용하여 이를 단축할 수 있습니다.
example (a b c : Nat) : a * (b * c) = a * (c * b) := by a:Natb:Natc:Nat⊢ a * (b * c) = a * (c * b)
conv in b * c => a:Natb:Natc:Nat| b * c
rw [Nat.mul_comm] a:Natb:Natc:Nat| c * b
이는 다음에 대한 문법적 설탕(syntax sugar)일 뿐입니다
example (a b c : Nat) : a * (b * c) = a * (c * b) := by a:Natb:Natc:Nat⊢ a * (b * c) = a * (c * b)
conv => a:Natb:Natc:Nat| a * (b * c) = a * (c * b)
pattern b * c a:Natb:Natc:Nat| b * c
rw [Nat.mul_comm] a:Natb:Natc:Nat| c * b
물론 와일드카드도 사용할 수 있습니다:
11.3. 변환 택틱 구성하기
중괄호와 .은 택틱을 구조화하기 위해 conv 모드에서도 사용할 수 있습니다:
example (a b c : Nat) : (0 + a) * (b * c) = a * (c * b) := by a:Natb:Natc:Nat⊢ (0 + a) * (b * c) = a * (c * b)
conv => a:Natb:Natc:Nat| (0 + a) * (b * c) = a * (c * b)
lhs a:Natb:Natc:Nat| (0 + a) * (b * c)
congr a a:Natb:Natc:Nat| 0 + aa a:Natb:Natc:Nat| b * c
. rw [Nat.zero_add] a a:Natb:Natc:Nat| a
. rw [Nat.mul_comm] a a:Natb:Natc:Nat| c * b
11.4. 변환 모드 안의 다른 택틱
-
argi는 애플리케이션의i번째 비의존적 명시적 인자로 진입합니다. -
args는congr의 대체 이름입니다. -
simp는 단순화기(simplifier)를 현재 목표에 적용합니다. 이는 일반 택틱 모드에서 사용 가능한 것과 동일한 옵션을 지원합니다.def f (x : Nat) := if x > 0 then x + 1 else x + 2example (g : Nat → Nat) (h₁ : g x = x + 1) (h₂ : x > 0) : g x = f x :=by x:Natg:Nat → Nath₁:g x = x + 1h₂:x > 0⊢ g x = f xconv => x:Natg:Nat → Nath₁:g x = x + 1h₂:x > 0| g x = f xrhs x:Natg:Nat → Nath₁:g x = x + 1h₂:x > 0| f xsimp [f, h₂] x:Natg:Nat → Nath₁:g x = x + 1h₂:x > 0| x + 1exact h₁ All goals completed! 🐙 -
해결되지 않은 목표가 있으면
done은 실패합니다. -
trace_state는 현재 택틱 상태를 표시합니다. -
whnf는 항을 약한 두부 정규형(weak head normal form)으로 변환합니다. -
tactic=> <tactic sequence>는 일반 택틱 모드로 돌아갑니다. 이는conv모드에서 지원되지 않는 목표를 해소하거나, 사용자 정의 합동 보조정리와 확장성 보조정리를 적용할 때 유용합니다.example (g : Nat → Nat → Nat) (h₁ : ∀ x, x ≠ 0 → g x x = 1) (h₂ : x ≠ 0) : g x x + x = 1 + x :=by x:Natg:Nat → Nat → Nath₁:∀ (x : Nat), x ≠ 0 → g x x = 1h₂:x ≠ 0⊢ g x x + x = 1 + xconv => x:Natg:Nat → Nat → Nath₁:∀ (x : Nat), x ≠ 0 → g x x = 1h₂:x ≠ 0| g x x + x = 1 + xlhs x:Natg:Nat → Nat → Nath₁:∀ (x : Nat), x ≠ 0 → g x x = 1h₂:x ≠ 0| g x x + xarg 1 x:Natg:Nat → Nat → Nath₁:∀ (x : Nat), x ≠ 0 → g x x = 1h₂:x ≠ 0| g x xrw [h₁] x:Natg:Nat → Nat → Nath₁:∀ (x : Nat), x ≠ 0 → g x x = 1h₂:x ≠ 0| 1a x:Natg:Nat → Nat → Nath₁:∀ (x : Nat), x ≠ 0 → g x x = 1h₂:x ≠ 0⊢ x ≠ 0 .skip x:Natg:Nat → Nat → Nath₁:∀ (x : Nat), x ≠ 0 → g x x = 1h₂:x ≠ 0| 1 .tactic => a x:Natg:Nat → Nat → Nath₁:∀ (x : Nat), x ≠ 0 → g x x = 1h₂:x ≠ 0⊢ x ≠ 0exact h₂ All goals completed! 🐙 -
apply<term>는tactic=> apply <term>의 문법적 설탕(syntax sugar)입니다.example (g : Nat → Nat → Nat) (h₁ : ∀ x, x ≠ 0 → g x x = 1) (h₂ : x ≠ 0) : g x x + x = 1 + x :=by x:Natg:Nat → Nat → Nath₁:∀ (x : Nat), x ≠ 0 → g x x = 1h₂:x ≠ 0⊢ g x x + x = 1 + xconv => x:Natg:Nat → Nat → Nath₁:∀ (x : Nat), x ≠ 0 → g x x = 1h₂:x ≠ 0| g x x + x = 1 + xlhs x:Natg:Nat → Nat → Nath₁:∀ (x : Nat), x ≠ 0 → g x x = 1h₂:x ≠ 0| g x x + xarg 1 x:Natg:Nat → Nat → Nath₁:∀ (x : Nat), x ≠ 0 → g x x = 1h₂:x ≠ 0| g x xrw [h₁] x:Natg:Nat → Nat → Nath₁:∀ (x : Nat), x ≠ 0 → g x x = 1h₂:x ≠ 0| 1a x:Natg:Nat → Nat → Nath₁:∀ (x : Nat), x ≠ 0 → g x x = 1h₂:x ≠ 0⊢ x ≠ 0 .skip x:Natg:Nat → Nat → Nath₁:∀ (x : Nat), x ≠ 0 → g x x = 1h₂:x ≠ 0| 1 .apply h₂ All goals completed! 🐙