我如何在 Idris interactive 中评估这个递归函数?

How do I evaluate this recursive function in Idris interactive?

涉足 Idris,我试图将 this Haskell function 移植到 Idris。我想我成功了,使用这段代码...

windowl : Nat -> List a -> List (List a)
windowl size = loop
  where
    loop xs = case List.splitAt size xs of
      (ys, []) => if length ys == size then [ys] else []
      (ys, _) => ys :: loop (drop 1 xs)

但是,当我在交互式 idris 中调用它时,似乎只对函数的第一次调用进行了评估,而递归的下一步则没有。这是我在控制台上得到的。

*hello> windowl 2 [1,2,3,4,5]
[1, 2] :: Main.windowl, loop Integer 2 [2, 3, 4, 5] : List (List Integer)

有人可以告诉我发生了什么以及我如何才能完全评估函数吗?

manual 中所述:

At compile-time it [Idris] will only evaluate things which it knows to be total ... [skipped]... The REPL, for convenience, uses the compile-time notion of evaluation.

完整性检查器无法发现 windowl 函数实际上是完整的,因此我们可以使用 assert_smaller construction:

total
windowl : Nat -> List a -> List (List a)
windowl size = loop
  where
    loop : List a -> List (List a)
    loop xs = case List.splitAt size xs of
      (ys, []) => if length ys == size then [ys] else []
      (ys, _) => ys :: loop (assert_smaller xs $ drop 1 xs)

或更改 loop 以使完整性对完整性检查器显而易见:

total
windowl : Nat -> List a -> List (List a)
windowl size = loop
  where
    loop : List a -> List (List a)
    loop [] = []
    loop xs@(x :: xs') = case List.splitAt size xs of
      (ys, []) => if length ys == size then [ys] else []
      (ys, _) => ys :: loop xs'

是的,我在这里偷工减料,用模式匹配替换硬编码的 drop 1 来说明这个想法。更一般的情况可能需要更多的工作。

此时,REPL 完全计算表达式:

λΠ> windowl 2 [1,2,3,4,5]
[[1, 2], [2, 3], [3, 4], [4, 5]] : List (List Integer)