给出列表的这些定义:
data List (A : Set) : Set where
[] : List A
_::_ : (x : A)(xs : List A) → List A
length : {A : Set} → List A → ℕ
length [] = zero
length (_ :: xs) = suc (length xs)
lookup : {A : Set}(xs : List A)(n : ℕ) → (isTrue (n < length xs)) → A
lookup [] n ()
lookup (x :: xs) zero p = x
lookup (x :: xs) (suc n) p = lookup xs n p
和布尔值:
data Bool : Set where
true : Bool
false : Bool
data False : Set where
record True : Set where
isTrue : Bool → Set
isTrue true = True
isTrue false = False
当_<_
定义为:
open import Data.Nat using (ℕ; suc; zero)
_<_ : ℕ → ℕ → Bool
_ < zero = false
zero < suc n = true
suc m < suc n = m < n
但是如果我将前两个定义的顺序切换为:
_<_ : ℕ → ℕ → Bool
zero < suc n = true
_ < zero = false
suc m < suc n = m < n
然后我得到了错误:
isTrue (n < length []) should be empty, but that's not obvious to me
when checking that the clause lookup [] n () has type
{A : Set} (xs : List A) (n : ℕ) → isTrue (n < length xs) → A
这向我表明n < length []
与zero < suc n = true
匹配,这不应该,因为length []
等于zero
或suc m < suc n = m < n
,它不应该匹配不会这样做,因为这是_<_
的最后一个定义(其他情况也是如此)。
我误解了agda如何与函数定义匹配,或者荒谬的模式如何工作?
答案 0 :(得分:3)
在内部,Agda通过将模式匹配转换为案例树来转换定义(请参见ReadTheDocs中的文档)。为了构造此案例树,Agda始终首先查看第一个子句。因此,根据条款的顺序,结果案例树可能会有所不同。在您的示例中,_<_
的第一个定义的案例树是(用伪语法):
m < n = case n of
zero -> false
suc n' -> case m of
zero -> true
suc m' -> m' < n'
_<_
的第二个定义的案例树为
m < n = case m of
zero -> case n of
zero -> false
suc n' -> true
suc m' -> case n of
zero -> false
suc m' -> m' < n'
实际上,在第二个定义中,Agda将单个子句_ < zero = false
分为两个子句zero < zero = false
和suc m < zero = false
,从而阻止n < zero
进一步缩小。
要阻止Agda像这样对子句进行拆分,可以将{-# OPTIONS --exact-split #-}
放在文件顶部(或将--exact-split
添加到Agda的命令行标志中)。
如果您想了解有关如何将模式匹配的定义详细化为案例树的更多信息,可以阅读关于主题的our upcoming ICFP paper。
答案 1 :(得分:2)
Jesper的正确答复还指出了使lookup
与_<_
的第二个定义一起工作的另一种方法:只需自己匹配n
即可清楚地知道{{1 }}:
_<_