关闭特定功能的FSharp功能缓存?

时间:2011-07-02 03:25:50

标签: f# memoization

我最近遇到了一个有趣但令人烦恼的F Sharp行为。根据[1],“F#自动缓存任何不带参数的函数的值。”这似乎是一个好主意,但是当我尝试提出一个包装函数来生成随机数时,它会给我带来麻烦。

作为一个例子,我在这个问题的最后代码中有两个不同的函数。第一个函数“getRand”不带参数,但不幸的是它总是返回相同的数字。第二个函数“getRand2”就像我希望每次调用时生成一个新的随机数一样,但是这会令人烦恼地忽略了额外的参数。

如果可能的话,我想拥有getRand2的功能但是getRand的便利性。是否有一个编译器指令或特殊关键字,我可以应用于getRand,它将关闭其功​​能缓存功能,从而使其行为像getRand2?

谢谢,

肖恩
注意:如果答案已经出现在[1]中,请原谅我,我现在没有看到它 [1] - http://en.wikibooks.org/wiki/F_Sharp_Programming/Caching

(* Always returns the same number *)
let getRand = 
   let seed = int32(System.DateTime.Now.Ticks)   
   let randGen = new System.Random(seed)
   randGen.Next()

(* Works as expected except I need an annoying extra parameter *)
let getRand2 dummyParam = 
   let seed = int32(System.DateTime.Now.Ticks)   
   let randGen = new System.Random(seed)
   randGen.Next()

(* Outputs three "identical" numbers to console *)
System.Console.WriteLine(
   "Parameterless getRand always outputs same number.")
System.Console.WriteLine(getRand)
System.Threading.Thread.Sleep(100)
System.Console.WriteLine(getRand)
System.Threading.Thread.Sleep(100)
System.Console.WriteLine(getRand)
System.Console.WriteLine()

(* Outputs three "different" numbers to console *)
System.Console.WriteLine(
   "GetRand2 works as expected even though second dummy param is always the same.")
System.Console.WriteLine(getRand2 0)
System.Threading.Thread.Sleep(100)
System.Console.WriteLine(getRand2 0)
System.Threading.Thread.Sleep(100)
System.Console.WriteLine(getRand2 0)
System.Console.WriteLine()

1 个答案:

答案 0 :(得分:6)

为了澄清一点,我认为短语“不带参数的功能”是误导性的。根据定义,函数将函数域中的值映射到函数范围中的值,因此所有函数都接受参数。在您的情况下,getRand未绑定到函数,它只是int类型的值。

如果我理解你的问题,我想你想做

let getRand =    
    let seed = int System.DateTime.Now.Ticks      
    let randGen = new System.Random(seed)   
    fun () -> randGen.Next()

您仍然需要将getRand作为一个函数调用(getRand(),而不仅仅是getRand),但是没有办法解决这个问题 - int这一事实值总是保持不变是推理程序的关键特征。

您可以使用getRand2函数,方法与我的getRand版本大致相同:因为您在正文中不使用dummyParam,因此F#使函数具有通用性,意味着您可以根据需要传递单位值()作为参数。 但是,您的getRand2功能已损坏,因为每次调用它时都会创建一个新的随机数生成器。这意味着如果你在一个刻度内调用它两次,你将得到相同的答案:

let x,y = (getRand2(), getRand2())

这就是为什么在匿名函数范围内定义<{1}}和seed 的重要性。