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相關的細微之處,我在這裏粉飾沒有意識到,但是這是大原則。