证明索引在给定的列表边界内 (index + 1) 在边界内
Proving an index is within list bounds given (index + 1) is within bounds
假设我们有一个 list : List a
,我们想使用它的第一个和第二个元素:
case inBounds 1 list of
Yes prf => doSmth (index 0 list) (index 1 list)
No _ => doSmthElse
不幸的是,这不会进行类型检查:确实,prf
是 InBounds 1 list
成立的证明,但是虽然对我们人类来说很明显 InBounds 0 list
紧随其后,但它仍然需要显示给类型检查器。
当然可以写个辅助证明
inBoundsDecr : InBounds (S k) xs -> InBounds k xs
inBoundsDecr {k = Z} (InLater y) = InFirst
inBoundsDecr {k = (S k)} (InLater y) = InLater (inBoundsDecr y)
然后在 Yes
分支中像这样使用它:
case inBounds 1 list of
Yes prf => let prf' = inBoundsDecr prf in
doSmth (index 0 list) (index 1 list)
No _ => doSmthElse
不过好像比较冗长
那么,在 Idris 中最简洁 and/or 惯用的方法是什么?
如果你有 xs
的证明,可以提供一些关于 xs
的信息,最好使用 with
而不是 case
。然后编译器可以看到参数可以通过证明来确定。本章教程见:Views and the "with" rule
addHs : List Nat -> Nat
addHs xs with (inBounds 1 xs)
addHs xs | p = ?hole
Pattern-matching 在 p
上给出 Yes prf
,然后是 Yes (InLater y)
,然后是 Yes (InLater InFirst)
,并将 xs
更新为 (x :: y :: xs)
.因此您可以轻松使用 x
和 y
:
addHs : List Nat -> Nat
addHs xs with (inBounds 1 xs)
addHs (x :: y :: xs) | (Yes (InLater InFirst)) = x + y
addHs xs | (No contra) = 0
问题是——在这里它不能正常工作。如果您检查 addHs
是否总计,编译器会警告您,因为它认为可能会达到另一个 Yes (InLater p)
。不知道为什么,完整性检查器不是那么好。但理论上它应该可以正常工作。 :-)
除了你最初的尝试(这可能更冗长,但你不太依赖完整性检查器),你当然可以添加一个虚拟 Yes (InLater p)
案例或停在那里做一些事情喜欢
addHs : List Nat -> Nat
addHs xs with (inBounds 1 xs)
addHs (x :: xs) | (Yes (InLater prf)) = x + (index 0 xs)
addHs xs | (No contra) = 0
或者有点不安全并断言该案例无法访问:
addHs : List Nat -> Nat
addHs xs with (inBounds 1 xs) proof q
addHs (x :: y :: xs) | Yes (InLater InFirst) = x + y
addHs (x :: xs) | Yes (InLater p) = assert_unreachable
addHs xs | No _ = 0
假设我们有一个 list : List a
,我们想使用它的第一个和第二个元素:
case inBounds 1 list of
Yes prf => doSmth (index 0 list) (index 1 list)
No _ => doSmthElse
不幸的是,这不会进行类型检查:确实,prf
是 InBounds 1 list
成立的证明,但是虽然对我们人类来说很明显 InBounds 0 list
紧随其后,但它仍然需要显示给类型检查器。
当然可以写个辅助证明
inBoundsDecr : InBounds (S k) xs -> InBounds k xs
inBoundsDecr {k = Z} (InLater y) = InFirst
inBoundsDecr {k = (S k)} (InLater y) = InLater (inBoundsDecr y)
然后在 Yes
分支中像这样使用它:
case inBounds 1 list of
Yes prf => let prf' = inBoundsDecr prf in
doSmth (index 0 list) (index 1 list)
No _ => doSmthElse
不过好像比较冗长
那么,在 Idris 中最简洁 and/or 惯用的方法是什么?
如果你有 xs
的证明,可以提供一些关于 xs
的信息,最好使用 with
而不是 case
。然后编译器可以看到参数可以通过证明来确定。本章教程见:Views and the "with" rule
addHs : List Nat -> Nat
addHs xs with (inBounds 1 xs)
addHs xs | p = ?hole
Pattern-matching 在 p
上给出 Yes prf
,然后是 Yes (InLater y)
,然后是 Yes (InLater InFirst)
,并将 xs
更新为 (x :: y :: xs)
.因此您可以轻松使用 x
和 y
:
addHs : List Nat -> Nat
addHs xs with (inBounds 1 xs)
addHs (x :: y :: xs) | (Yes (InLater InFirst)) = x + y
addHs xs | (No contra) = 0
问题是——在这里它不能正常工作。如果您检查 addHs
是否总计,编译器会警告您,因为它认为可能会达到另一个 Yes (InLater p)
。不知道为什么,完整性检查器不是那么好。但理论上它应该可以正常工作。 :-)
除了你最初的尝试(这可能更冗长,但你不太依赖完整性检查器),你当然可以添加一个虚拟 Yes (InLater p)
案例或停在那里做一些事情喜欢
addHs : List Nat -> Nat
addHs xs with (inBounds 1 xs)
addHs (x :: xs) | (Yes (InLater prf)) = x + (index 0 xs)
addHs xs | (No contra) = 0
或者有点不安全并断言该案例无法访问:
addHs : List Nat -> Nat
addHs xs with (inBounds 1 xs) proof q
addHs (x :: y :: xs) | Yes (InLater InFirst) = x + y
addHs (x :: xs) | Yes (InLater p) = assert_unreachable
addHs xs | No _ = 0