Idris实际上不会将fibo
函数视为总共有两个原因。首先,正如你所指出的,它没有定义小于1的整数,但其次,它自己递归地调用它自己。虽然Idris能够识别整个递归函数,但通常只有在能够显示递归调用的参数比原始参数“更小”(即更接近基本情况*)时才可以这样做(例如,如果一个函数收到一个列表作为参数,它可以使用列表的尾部调用它自己而不必牺牲整体性,因为尾部是原始列表的子结构,因此更接近于Nil
)。与像(n-1)
(n-2)
和,表达的问题,当它们是Int
型的是,虽然它们是数值小于n,它们不是结构小一些,因为Int
不感应地定义,因此没有基的情况。因此总体检查者无法确定递归总是最终会达到基本情况(即使它对我们来说可能很明显),所以它不会认为总数是fibo
。
首先,我们来解决递归问题。代替Int
,我们可以使用一个电感定义的数据类型如Nat
:
data Nat =
Z | S Nat
(A自然数是零,或另一个自然数的继任者。)
这使我们能够改写fibo
如:
fibo : Nat -> Int
fibo (S Z) = 1
fibo (S (S Z)) = 2
fibo (S (S n)) = fibo (S n) + fibo n
(注意,而不是计算(n-1)
和(n-2)
明确如何在递归的情况下,我们生产他们通过图案参数匹配,从而DEM onstrating到伊德里斯它们在结构上是较小的。)
这种新的fibo
定义仍然不完全总,但是,因为它缺乏对Z
的情况下(即零)。如果我们不想提供这样的情况,那么我们需要给伊德里斯一些保证它不会被允许发生。我们能做到这一点的方法之一是需要证明的参数fibo
大于或等于一(或等价地,一个小于或等于参数):
fibo : (n : Nat) -> LTE 1 n -> Int
fibo Z LTEZero impossible
fibo Z (LTESucc _) impossible
fibo (S Z) _ = 1
fibo (S (S Z)) _ = 2
fibo (S (S (S n))) _ = fibo (S (S n)) (LTESucc LTEZero) + fibo (S n) (LTESucc LTEZero)
LTE 1 n
是它的类型值是证明1≤n(在自然数内)。 LTEZero
表示≤任意的自然数为零的公理,和LTESucc
表示规则,如果N≤米,然后(的Ñ后继)≤(的米后继)。关键字impossible
表示不会发生特定情况。在上面的定义中,fibo
的第一个参数不可能是零,因为没有办法证明1≤0。对于任何其他自然数n,我们可以证明1≤n使用(LTESucc LTEZero)
。
现在终于fibo
是总的,但它是相当麻烦不得不为它提供一个明确的证据证明它的说法是大于或等于1。幸运的是,我们可以标记证明论点的汽车隐:
fibo : (n : Nat) -> {auto p : LTE 1 n} -> Int
fibo Z {p = LTEZero} impossible
fibo Z {p = (LTESucc _)} impossible
fibo (S Z) = 1
fibo (S (S Z)) = 2
fibo (S (S (S n))) = fibo (S (S n)) + fibo (S n)
伊德里斯现在会自动找到1≤n的证明,否则我们仍然会被要求自己提供一个。
*有可能是一些CODATA相关的细微之处,我在这里粉饰没有意识到,但是这是大原则。