You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在全函数中匹配整数范围?Int奇偶性实现疑难

咱们一步步拆解你遇到的Int奇偶性全函数实现问题,顺便理清那些让人困惑的细节:

1. 为什么parity1不是全函数?

你说得对,Idris的Int类型的mod并非全函数——这是因为Int是有界整数,当你对minBound(比如64位系统下的-2^63)执行mod 2时,会出现溢出,导致无法得到合法结果。而REPL显示mod是全函数,是因为Integral接口的签名是全函数,但具体到Int的实现是部分的,这确实有点反直觉,但Idris允许接口的实现是部分函数(虽然不推荐)。

2. 如何让parity3成为全函数?

DivBy视图返回的DivBy prf里包含了关键信息:余数rem满足0 ≤ rem < 2(也就是rem_lt : rem < 2)。我们可以利用这个证明,用absurd消除掉rem既不是0也不是1的不可能分支,让编译器认可这是全函数:

data Parity = Even | Odd

parity3 : Int -> Parity
parity3 x with (divides x 2)
parity3 ((2 * div) + rem) | (DivBy div rem x_eq rem_lt) = 
    case decEq rem 0 of
        Yes _ => Even
        No not_zero => case decEq rem 1 of
            Yes _ => Odd
            No not_one => absurd $ ltNotGe rem_lt not_one

这里的ltNotGe是Idris标准库的定理,用来证明如果a < b,那么a ≥ b是不可能的。因为rem < 2,而not_one证明了rem ≠ 1,结合not_zero的rem ≠ 0,就意味着rem ≥ 2,这和rem_lt矛盾,所以这个分支不可能存在,用absurd就能消除它。

3. 为什么parity4被认定为全函数?这是bug吗?

这不是bug,而是Idris视图匹配和impossible关键字的语义导致的:

  • divides x 2对于任意Int x都是全的(因为除数2非零),所以with块只会进入DivBy分支,没有其他情况。
  • impossible关键字告诉编译器:"这个分支不可能被执行",所以编译器认为函数没有遗漏分支,是全函数。但实际上,所有调用都会进入这个分支,运行时会触发impossible的崩溃错误。

这本质上是误用了impossible——它应该用来标记逻辑上不可能存在的分支,而这里的分支是必然存在的,所以运行时会出错。

4. 用Integer能否简化问题?

当然可以!Integer是无界整数,它的mod是全函数,所以直接用你最初的parity1风格就能写出全函数:

parityInteger : Integer -> Parity
parityInteger x = if mod x 2 == 0 then Even else Odd

如果你还是想用DivBy视图,Integer的divides同样能提供合法的余数证明,而且处理起来更直观,因为Integer的正负号逻辑不会干扰余数的范围(余数总是非负且小于除数)。

内容的提问来源于stack exchange,提问作者Pavel Strakhov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 19:27:58