如何在F#中设置默认参数值?

17

以这个函数为例:

// Sequence of random numbers
open System

let randomSequence m n = 
    seq { 
        let rng = Random ()
        while true do
            yield rng.Next (m, n)
    }


randomSequence 8 39

randomSequence函数接受两个参数:m, n。正常情况下,这个函数能够正常工作。我希望可以为m, n设置默认值,例如:

(m = 1, n = 100)

当没有给出参数时,函数会采用默认值。在F#中是否可能实现这个功能?


1
这有点棘手,因为涉及到柯里化。 - John Palmer
4个回答

18
您好,以下是翻译内容:

您可以经常使用带有辨别联合的重载来达到相同的效果

以下是基于原作者的建议:

type Range = Default | Between of int * int

let randomSequence range = 
    let m, n =
        match range with
        | Default -> 1, 100
        | Between (min, max) -> min, max

    seq {
        let rng = new Random()
        while true do
            yield rng.Next(m, n) }

请注意引入了Range枚举类型。
以下是一些使用(FSI)的示例:
> randomSequence (Between(8, 39)) |> Seq.take 10 |> Seq.toList;;
val it : int list = [11; 20; 36; 30; 35; 16; 38; 17; 9; 29]

> randomSequence Default |> Seq.take 10 |> Seq.toList;;
val it : int list = [98; 31; 29; 73; 3; 75; 17; 99; 36; 25]

另一种选择是稍微改变randomSequence函数,使其接受元组而不是两个值:

let randomSequence (m, n) = 
    seq {
        let rng = new Random()
        while true do
            yield rng.Next(m, n) }

这将允许您定义一个默认的,例如:
let DefaultRange = 1, 100

以下是一些(FSI)使用示例:

> randomSequence (8, 39) |> Seq.take 10 |> Seq.toList;;
val it : int list = [30; 37; 12; 32; 12; 33; 9; 23; 31; 32]

> randomSequence DefaultRange |> Seq.take 10 |> Seq.toList;;
val it : int list = [72; 2; 55; 88; 21; 96; 57; 46; 56; 7]

调用类型为 Between (x, y)Default 的函数很麻烦,但这几乎是最好的解决方案。谢谢! - Nick
3
@Nick 或许有点繁琐,但就我的经验而言,切换到联合判别式有时会触发关于域的新想法,并且新的联合判别式还可以在其他函数中使用,就像链接示例所说明的那样。无论如何,我已经用另一种选择更新了我的答案。 - Mark Seemann
非常感谢。实际上,在Python或R中,调用此函数的常规方式为:(1)randomSequence(8, 39),(2)randomSequence(39),(3)randomSequence()。请注意,第二个调用等同于randomSequence(1, 39)。在第三个调用中,我没有指定任何内容 - 只是空的(). - Nick

16

仅允许在成员上使用可选参数[...]

https://msdn.microsoft.com/zh-cn/library/dd233213.aspx

所以,对于您目前的示例,我认为这是不可能的。 但是,您可以将功能进行封装:

type Random =
    static member sequence(?m, ?n) =
        let n = defaultArg n 100
        let rng = new System.Random()
        match m with
        | None -> Seq.initInfinite (fun _ -> rng.Next(1, n))
        | Some(m) -> Seq.initInfinite (fun _ -> rng.Next(m, n))

然后像这样使用它:

let randomSequence = Random.sequence(2, 3)
let randomSequence' = Random.sequence(n = 200)
let randomSequence'' = Random.sequence()

说明:可选参数可以是完全可选m)或默认参数n)。我喜欢使用阴影效果(重用相同的名称)来表示这些参数,但这是有争议的。


10

这似乎是解决这个问题最优雅的方法:

//define this helper function
let (|Default|) defaultValue input =
    defaultArg input defaultValue

//then specify default parameters like this
let compile (Default true optimize) = 
    optimize

//or as the OP asks
let randomSequence (Default 1 m) (Default 100 n) =
  seq { 
      let rng = new System.Random()
      while true do
          yield rng.Next(m,n)
  }

Credits: http://fssnip.net/5z


4
let randomSequence m n= 
    seq { 
    let rng = new Random()
    while true do
        yield rng.Next(m,n)
    }

let randomSequenceWithDefaults() =
    randomSequence 1 100

因此,我们应该调用 randomSequenceWithDefaults() 代替。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接