How to set default argument value in F#?

折月煮酒 提交于 2019-12-22 03:22:41

问题


Take this function as an example:

// Sequence of random numbers
open System

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


randomSequence 8 39

The randomSequence function takes two arguments: m, n. This works fine as a normal function. I would like to set the default for m, n, for example:

(m = 1, n = 100)

When there's no arguments given, the function take the default value. Is it possible in F#?


回答1:


You can often achieve the same effect as overloading with a Discriminated Union.

Here's a suggestion based on the OP:

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) }

Notice the introduction of the Range Discriminated Union.

Here are some (FSI) examples of usage:

> 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]

Another option is to change the randomSequence ever so slightly to take a tuple instead of two values:

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

This would allow you to also define a default value, like this:

let DefaultRange = 1, 100

Here are some (FSI) examples of usage:

> 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]



回答2:


Optional parameters are permitted only on members [...]

https://msdn.microsoft.com/en-us/library/dd233213.aspx

So, for your current example, I think it is impossible. But you could wrap the functionality:

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))

and then use it like this:

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

Explanation: optional parameters can either be fully Optional (m) or defaultArgs (n). I like shadowing (reusing the same name) these parameters, but that's debatable.




回答3:


This seems to be the most elegant solution to this problem:

//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 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

So instead, call randomSequenceWithDefaults()



来源:https://stackoverflow.com/questions/30255271/how-to-set-default-argument-value-in-f

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!