F#多条件if/else与匹配

nat*_*han 4 f# functional-programming

我是F#的新手,并且一直在实现简单的算法来学习语言结构.我使用了bisection方法if/else,然后想要学习如何使用匹配.

if fc = 0.0                   then printfn "%i/%i - f(%f) = %f, f(%f) = %f, f(%f) = %f" new_count n a fa b fb c fc
else if ((b - a) * 0.5) < eps then printfn "%i/%i - f(%f) = %f, f(%f) = %f, f(%f) = %f" new_count n a fa b fb c fc
else if new_count = n         then printfn "%i/%i - f(%f) = %f, f(%f) = %f, f(%f) = %f" new_count n a fa b fb c fc
else if fc * fa < 0.0         then bisect a c new_count
else if fc * fb < 0.0         then bisect c b new_count 
Run Code Online (Sandbox Code Playgroud)

我发现使用match a, b, fa, fb, fc会导致类型错误,如果我只使用一个参数,我基本上可以忽略参数并检查我的条件.什么是惯用的F#/功能方式来使用匹配?或者我应该坚持if/else?

 match a with 
    | a when fc = 0.0              ->  printfn "%i/%i - f(%f) = %f, f(%f) = %f, f(%f) = %f" new_count n a fa b fb c fc
    | a when ((b - a) * 0.5) < eps -> printfn "%i/%i - f(%f) = %f, f(%f) = %f, f(%f) = %f" new_count n a fa b fb c fc
    | a when new_count = n         -> printfn "%i/%i - f(%f) = %f, f(%f) = %f, f(%f) = %f" new_count n a fa b fb c fc
    | a when fc * fa < 0.0         -> bisect a c new_count
    | a when fc * fb < 0.0         -> bisect c b new_count 
Run Code Online (Sandbox Code Playgroud)

Fyo*_*kin 8

你的条件都处理不同的事情,彼此无关,所以ifs 串就好了.我唯一推荐的是使用elif而不是else if.

match应该理解为"给予这种可以有不同风味的东西,这里是如何处理这些风味".一个特别的优点match是编译器会弄清楚,并告诉你,你是否错过了任何"风味".特别是,您在问题中提供的代码应该产生编译器警告,抱怨" 不完整模式匹配此表达式 ".想想看:当没有一个案例匹配时,该表达式的结果是什么?

对于ifs,情况也是如此.例如,这不编译:

let x = if a < 5 then 7
Run Code Online (Sandbox Code Playgroud)

为什么?因为编译器知道结果应该是什么a < 5(即它应该是7),但是它应该是什么呢?编译器无法为您决定,因此会生成错误.
另一方面,这将编译:

let x = if a < 5 then 7 else 8
Run Code Online (Sandbox Code Playgroud)

但是在你的特殊情况下,编译器可以让你逃避这一点,因为你的所有分支都返回一个unit(为什么?因为printf返回unit,所有其他分支都是递归的).换句话说,以下将编译:

let x = if a < 5 then ()
Run Code Online (Sandbox Code Playgroud)

以下内容:

let x = if a < 5 then printf "boo!"
Run Code Online (Sandbox Code Playgroud)

编译器让你逃避这一点,因为unit它是特殊的:它只能有一个值(即()),所以编译器可以决定当条件不是时表达式的结果是什么true.

一个实际的结果是,如果你没有非常仔细地考虑你的情况,可能会发生这样的事情,以至于没有你的条件true,所以整个事情将返回unit而不打印任何东西.我不能说在你的特定情况下是否会发生这种情况,因为我没有看到整个函数定义.


Mar*_*ann 5

有时,由于陀Soikin正确的解释,一系列的ifelse ifelse表达式是最好的选择,但我会用elif代替else if

有时是什么意义是之前计算一些值,并把它们放入一个数据结构,你可以匹配-通常是一个元组。

使用上述问题的简化版本,假设您只需要检查前两种情况,就可以这样做:

match fc = 0., ((b - a) * 0.5) < eps with
| true, _ -> "fc is 0"
| _, true -> "((b - a) * 0.5) is less than eps"
| _ -> "etc."
Run Code Online (Sandbox Code Playgroud)

请注意后面的逗号fc = 0.,它使match表达式成为一个元组,更具体地说是a bool * bool

这样做的缺点是效率低下,因为即使计算结果为,您也会一直在评估表达式。((b - a) * 0.5) < epsfc = 0.true

尽管如此,评估这样的简单表达式((b - a) * 0.5) < eps将是如此之快,以至于您可能无法测量它,因此,如果您认为这种表达算法的方式更具可读性,则可以决定权衡这种低效率以提高可读性。

在这种情况下,虽然,我不认为这是更具可读性,所以我还是用一系列的去ifelifelse表达式。

这是一个示例,其中预先计算值并将其放入元组更有意义:

match number % 3, number % 5 with
| 0, 0 -> "FizzBuzz"
| _, 0 -> "Buzz"
| 0, _ -> "Fizz"
| _    -> number.ToString()
Run Code Online (Sandbox Code Playgroud)

这是FizzBu​​zz kata的常见实现。在这里是有道理的,因为第一个匹配需要两个模数,所以没有效率低下,代码也很可读。


上面关于低效率的观点对于F#是正确的,因为对F#的评估很热。另一方面,在Haskell中,表达式是延迟计算的,因此您可以执行以下操作而不会降低效率:

case (fc == 0.0, ((b - a) * 0.5) < eps) of
  (True, _) -> "fc is 0"
  (_, True) -> "((b - a) * 0.5) is less than eps"
  _ -> "etc."
Run Code Online (Sandbox Code Playgroud)

元组中的第二个表达式仅在必要时才求值,因此,如果第一个case((True, _))匹配,则无需评估第二个表达式。