我是F#的新手,并且一直在实施简单的算法来学习语言结构。我使用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
我发现使用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
答案 0 :(得分:7)
你的条件都处理不同的事情,彼此无关,所以if
的字符串就好了。我建议的唯一方法是使用elif
代替else if
。
match
应该被理解为“鉴于这种东西可以是不同的味道,这里是如何处理这些味道”。 match
的一个特殊优势是编译器会弄清楚,并告诉你,你是否错过了任何“风味”。特别是,您在问题中提供的代码应该产生编译器警告,抱怨“不完整模式匹配此表达式”。想一想:当没有一个案例匹配时,那个表达式的结果会是什么?
对于if
s,情况也是如此。例如,这不编译:
let x = if a < 5 then 7
为什么呢?因为编译器在a < 5
(即它应该是7
)时知道结果应该是什么,但是它应该是什么呢?编译器无法为您决定,因此会产生错误
另一方面,这将编译:
let x = if a < 5 then 7 else 8
但是在你的特定情况下,编译器可以让你逃避这一点,因为你的所有分支都返回unit
(为什么?因为printf
返回unit
,而所有其他分支都是递归的)。换句话说,以下将编译:
let x = if a < 5 then ()
以下内容:
let x = if a < 5 then printf "boo!"
编译器可以让你逃避这一点,因为unit
很特殊:它只能有一个值(即()
),因此编译器可以决定对于你,当条件不是true
时,表达式的结果是什么。
这样做的一个实际结果就是,如果你没有仔细考虑你的情况非常谨慎,那么可能会发生这样的事情,因此你的条件都不是true
,所以整件事将返回unit
并且不会打印任何内容。我不能说在你的特定情况下是否会发生这种情况,因为我没有看到整个函数的定义。
答案 1 :(得分:4)
有时候,正如Fyodor Soikin正确解释的那样,尽管我使用if
代替else if
,else
,elif
,else if
表达式是最佳选择。 {1}}。
有时有意义的是先计算一些值,然后将它们放入一个可以匹配的数据结构中 - 通常是一个元组。
使用上述问题的简化版本,假设您只需检查前两种情况,可以这样做:
match fc = 0., ((b - a) * 0.5) < eps with
| true, _ -> "fc is 0"
| _, true -> "((b - a) * 0.5) is less than eps"
| _ -> "etc."
注意fc = 0.
之后的逗号,它将匹配表达式转换为元组 - 更具体地说是bool * bool
。
这样做的缺点是效率低下,因为即使((b - a) * 0.5) < eps
评估为{,总是正在评估表达式fc = 0.
{1}}。
仍然,评估像true
这样的简单表达式会非常快,以至于你可能无法测量它,所以如果你认为这种表达算法的方式更具可读性,你可以决定交易关闭这种低效率以提高可读性。
在这种情况下,我不认为它更具可读性,所以我仍然会使用一系列((b - a) * 0.5) < eps
,if
,elif
表达式。
这是一个预先计算值并将它们放入元组更有意义的示例:
else
这是FizzBuzz kata的常见实现。这有意义,因为第一次匹配需要两个模数,所以没有低效率,代码也很可读。
关于效率低下的观点对于F#来说是正确的,因为热切地评估了F#。另一方面,在Haskell中,表达式被懒惰地评估,因此您可以在不损失效率的情况下执行以下操作:
match number % 3, number % 5 with
| 0, 0 -> "FizzBuzz"
| _, 0 -> "Buzz"
| 0, _ -> "Fizz"
| _ -> number.ToString()
元组中的第二个表达式只会在必要时进行评估,因此如果第一个案例(case (fc == 0.0, ((b - a) * 0.5) < eps) of
(True, _) -> "fc is 0"
(_, True) -> "((b - a) * 0.5) is less than eps"
_ -> "etc."
)匹配,则无需评估第二个表达式。