我正在尝试在Idris中定义一个函数(congruentialMethod
),其中包括对我的函数提供的一些参数应用模运算。模数运算要求第二个参数为非零,因此我尝试使用modNatNZ
并向我的函数类型签名添加证明。但是,我觉得我的尝试非常笨重。
首先,当我定义最终成为模数函数(let m : Nat = 2147483647
)的第二个arg的值时,-I必须执行一些笨重的大小写匹配以说服Idris该值大于零。如果我不这样做,那么类型检查需要永远。
其次,当我调用我的函数(congruentialMethod
)时,我真的不明白为什么必须提供证据。我在功能签名中将证明指定为auto
,认为Idris能够推断它。
module Random
%default total
getUnixEpoch : IO Nat
getUnixEpoch = do time <- foreign FFI_C "#(unsigned long)time(NULL)" (IO Int)
pure $ fromInteger $ cast time
congruentialMethod : (seed : Nat) -> (a : Nat) -> (b : Nat) -> (m : Nat) -> {auto prf : (m = Z) -> Void} -> Stream Double
congruentialMethod seed a b m {prf} =
(cast seed) / (cast m) :: loop seed
where
loop : (prev : Nat) -> Stream Double
loop prev = let n = modNatNZ (a * prev + b) m prf
in (cast n) / (cast m) :: loop n
main : IO ()
main = do time <- getUnixEpoch
putStrLn $ "seed: " ++ (cast time)
let a : Nat = 16807
let b : Nat = 0
let m : Nat = 2147483647
case m of
Z => ?shouldnt_happen
S m' => do let random_number_stream = congruentialMethod time a b (S m') {prf = SIsNotZ}
?continue
我可以以某种方式避免将证据传递给我的congruentialMethod
函数吗?是否有一种不那么笨重的方式来说服伊德里斯let m : Nat = 2147483647
大于零(而不是使用案例匹配)?
编辑:此代码的另一个问题似乎是使用Nat
执行计算的速度非常慢。使用Int
,mod
并将函数更改为partial
可以快速生成数字。显然被迫将函数定义为partial
很糟糕......但也许这对另一个问题来说很重要......
答案 0 :(得分:3)
Idris很难推断let fileLines = unsolved.compactMap{ Int(String($0) }
// fileLines = [1, 2, 3]
样张,因为它们是函数,而Idris只是不试图推断函数,因为函数非常复杂。标准库已经考虑过你的情况了。我们有Prelude.Nat.IsSucc
:
Not
这适用于data IsSucc : Nat -> Type where
ItIsSucc : IsSucc (S n)
,但现有的auto
函数与它不兼容,因此我们使用了一些粘合剂。
Nat
就是这样:
isSuccNotZero : IsSucc n -> Not (n = Z)
isSuccNotZero {n = S _} ItIsSucc Refl impossible
使用网站现在将推断congruentialMethod : (seed : Nat) -> (a : Nat) -> (b : Nat) ->
(m : Nat) -> {auto prf : IsSucc m} -> Stream Double
congruentialMethod seed a b m {prf} =
(cast seed / cast m) :: (congruentialMethod (modNatNZ (a * seed + b) m $ isSuccNotZero prf) a b m)
。但是,如果编号很大,编译器仍然会扼杀,正如您在编辑中看到的那样。它最终会做对,但这需要一段时间。我没有解决方案。