这个递归函数不是完全的,还是编译器无法证明它?如何将其重写为总计?

时间:2016-08-20 16:00:14

标签: recursion pattern-matching idris totality

如果出现以下代码:

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将它识别为总数?

2 个答案:

答案 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,我们可以在每一步减少,确保我们停在0n的初始值自然可以是输入列表的长度。