如果出现以下代码:
module TotalityOrWhat
%default total
data Instruction = Jump Nat | Return Char | Error
parse : List Char -> Instruction
parse ('j' :: '1' :: _) = Jump 1
parse ('j' :: '2' :: _) = Jump 2
parse ('j' :: '3' :: _) = Jump 3
parse ('r' :: x :: _) = Return x
parse _ = Error
parseDriver : String -> Maybe Char
parseDriver = parseDriver' . unpack where
parseDriver' : List Char -> Maybe Char
parseDriver' xs =
case parse xs of
(Jump j) => parseDriver' $ drop j xs
(Return x) => Just x
Error => Nothing
Idris发出错误:
TotalityOrWhat.idr:17:3: TotalityOrWhat.parseDriver, parseDriver' is possibly not total due to recursive path TotalityOrWhat.parseDriver, parseDriver' --> TotalityOrWhat.parseDriver, parseDriver' TotalityOrWhat.idr:15:1: TotalityOrWhat.parseDriver is possibly not total due to: TotalityOrWhat.parseDriver, parseDriver'
我已经用其他几种方式重写了这段代码,但无法让Idris将其识别为总数。我错了它是完全的,还是伊德里斯不能确定它是什么?如果它本质上是完全的,我怎么能重写它以便Idris将它识别为总数?
答案 0 :(得分:3)
这不是答案"为什么它不被认为是全部"并且甚至没有回答"如何将其更改为被识别为总数",但是因为你提到了
我已经用其他几种方式重写了这段代码,但无法让Idris将其识别为总数,
我认为您可能对解决方法感兴趣。如果您手动将parse
内联到parseDriver'
,则可以通过整体检查程序:
total parseDriver : String -> Maybe Char
parseDriver = parseDriver' . unpack where
parseDriver' : List Char -> Maybe Char
parseDriver' ('j' :: '1' :: xs) = parseDriver' ('1' :: xs)
parseDriver' ('j' :: '2' :: xs) = parseDriver' xs
parseDriver' ('j' :: '3' :: _ :: xs) = parseDriver' xs
parseDriver' ('r' :: x :: _) = Just x
parseDriver' _ = Nothing
这是有效的,因为我们总是在xs
的某些后缀上进行结构上的递归。
答案 1 :(得分:3)
这里的问题是,Idris无法知道drop j xs
从输入中产生一个严格较小的列表,因为drop
的类型不够表达。
因此,另一个 ad-hoc 方法是使用一个伪参数,通过在调用xs'
时使用结构较小的列表parseDriver'
,使整体检查器接受该函数递归。
parseDriver : String -> Maybe Char
parseDriver s = parseDriver' chars chars where
chars : List Char
chars = unpack s
-- 2nd parameter is a dummy one (to make totality checker happy)
parseDriver' : List Char -> List Char -> Maybe Char
parseDriver' _ [] = Nothing
parseDriver' xs (_::xs') =
case parse xs of
Jump j => parseDriver' (drop j xs) xs'
Return x => Just x
Error => Nothing
我们还可以使用一些自然参数n
,我们可以在每一步减少,确保我们停在0
。 n
的初始值自然可以是输入列表的长度。