我一直在玩自己的随机数发生器在Idris中进行学习。在我的解决方案中,我的目标是所有功能的总和,因此,我使用类型为Nat
的数字和内置函数modNatNZ
,该函数需要证明第二个arg不为零。
在编写程序来测试一些大自然数作为输入的函数时,我遇到了类型检查和程序执行都非常慢的问题。
module Main
%default total
getUnixEpoch : IO Int
getUnixEpoch = foreign FFI_C "#(unsigned long)time(NULL)" (IO Int)
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 (safeMod_m (a * seed + b)) a b m
where
safeMod_m : Nat -> Nat
safeMod_m x = modNatNZ x m (isSuccNotZero prf)
randomNumberGenerator : (seed : Nat) -> Stream Double
randomNumberGenerator seed =
let a : Nat = 16807
b : Nat = 0
m : Nat = 2147483647
in
case m of
Z => ?this_will_never_happen_but_it_makes_type_checking_faster
(S m') => congruentialMethod seed a b (S m')
main : IO ()
main = do seed <- getUnixEpoch
putStrLn $ show $ take 5 $ randomNumberGenerator (cast seed)
类型检查器似乎花了很长时间才能验证2147483647的硬编码值确实大于零。我对此的较差解决方法是说服Idris使用案例表达。
...
m = 2147483647
in
case m of
Z => ?this_will_never_happen_but_it_makes_type_checking_faster
(S m') => congruentialMethod seed a b (S m')
很显然,我对case表达式的解决方法并不十分令人满意。那么,是否有更好的方法说服类型检查器m大于零,以便更快地进行类型检查?
如果这是需要解决方法的问题,那么我想知道这是否是理论上类型检查器的未来实现是否可以在合理的时间内处理而无需解决方法,或者-如果这是我解决的问题应该总是希望能变通以获得快速的类型检查?
我试图在repl中执行该程序并执行该程序的编译版本,但是由于它们似乎要花很长时间才能运行,因此不得不手动终止它们。
我做了一些实验,制作了一个使用整数(Int
类型)的程序,该程序能够获得快速的运行时性能,但是我看不到任何实现所有功能的方法用整数制作相同程序时,total
。
有没有一种方法可以定义我的程序,并且所有功能均为total
,并且仍然可以获得快速的性能?
同样,如果当前尚没有一种很好的方法来提高此类程序的运行时性能,那么我想知道这是否最终/理论上可以在以后的Idris版本中得到改进,-还是这是我总是必须解决或退回到部分函数才能获得快速运行时性能的方法?
我正在使用Idris版本1.2.0
答案 0 :(得分:1)
考虑
N : Nat
N = 10000
nIsS : IsSucc N
nIsS = ItIsSucc
使用
ItIsSucc : IsSucc (S n)
就我所知,目前,在进行类型检查时必须对所有内容进行评估,即必须构造10000
才能找到n
。它们都没有优化为整数(在编译时阶段),而是嵌套了S (S (S (S (S …))))
。您可以%freeze N
停止对其进行评估(在您的情况下适用):
N : Nat
N = 9999
%freeze N
nIsS : IsSucc (S N)
nIsS = ItIsSucc
从理论上讲,这不是必需的,因为您可以从第一个S (…)
轻松地看到它满足IsSucc (S n)
(只需设置n = …
)即可。这种懒惰的统一方法称为weak head normal form。 WHNF在某种程度上实现了,但是显然在类型检查时并不总是使用WHNF。因此,将来可以对此进行改进,如果我没记错的话,Blodwen(WIP!)支持这一点。至少有一个bigsuc-example。 :-)
Idris将Nat
编译为GMP bignums并为其使用相应的C函数(例如加法,减法,乘法等)。模数不在其中,但使用actual Idris definition。当然,这没有Integer
快,后者使用本机C函数。
实际上,您对此无能为力。如果您确实想要总计,那么如果您真的确定此功能是总计,最好的办法就是手动声明它:
modIntNZ : (i, m : Integer) -> {auto prf : m == 0 = False} -> Integer
modIntNZ i m = assert_total (i `mod` m)