模式匹配与守卫vs在F#中的if / else构造

时间:2011-11-02 20:04:32

标签: f# functional-programming pattern-matching guard-clause

在ML系列语言中,人们倾向于选择模式匹配到if/else构造。在F#中,在模式匹配中使用保护可以在很多情况下轻松替换if/else

例如,可以在不使用delete1的情况下重写简单的if/else函数(请参阅delete2):

let rec delete1 (a, xs) =
    match xs with
    | [] -> []
    | x::xs' -> if x = a then xs' else x::delete1(a, xs') 

let rec delete2 (a, xs) =
    match xs with
    | [] -> []
    | x::xs' when x = a -> xs'
    | x::xs' -> x::delete2(a, xs') 

另一个例子是求解二次函数:

type Solution =
    | NoRoot
    | OneRoot of float
    | TwoRoots of float * float

let solve1 (a,b,c) = 
    let delta = b*b-4.0*a*c
    if delta < 0.0 || a = 0.0 then NoRoot 
    elif delta = 0.0 then OneRoot (-b/(2.0*a))
    else 
        TwoRoots ((-b + sqrt(delta))/(2.0*a), (-b - sqrt(delta))/(2.0*a))

let solve2 (a,b,c) = 
    match a, b*b-4.0*a*c with
    | 0.0, _  -> NoRoot
    | _, delta when delta < 0.0 -> NoRoot
    | _, 0.0 -> OneRoot (-b/(2.0*a))
    | _, delta -> TwoRoots((-b + sqrt(delta))/(2.0*a),(-b - sqrt(delta))/(2.0*a))

我们是否应该使用与守卫的模式匹配来忽略丑陋的if/else构造?

使用防护模式匹配是否有任何性能影响?我的印象是它似乎很慢,因为在运行时检查了模式匹配。

4 个答案:

答案 0 :(得分:9)

两者都有自己的位置。人们更习惯使用If / else构造来检查一个值,其中模式匹配就像类固醇上的If / else一样。模式匹配允许您对数据的decomposed结构进行排序,并使用gaurds来指定分解数据部分或其他值的某些附加条件(特别是在递归数据结构或所谓的情况下) F#中有歧视的工会。

我个人更喜欢使用if / else进行简单的值比较(true / false,ints等),但是如果你有一个递归数据结构或需要与其分解的值进行比较的东西比没有什么比模式匹配。

首先使它工作并使其优雅和简单然后如果你看起来有一些性能问题,那么检查性能问题(这主要是由于一些其他逻辑而不是由于模式匹配)

答案 1 :(得分:7)

正确的答案可能是它取决于,但我猜测,在大多数情况下,编译的表示是相同的。作为一个例子

let f b =
  match b with
  | true -> 1
  | false -> 0

let f b =
  if b then 1
  else 0

都转换为

public static int f(bool b)
{
    if (!b)
    {
        return 0;
    }
    return 1;
}

鉴于此,这主要是风格问题。我个人更喜欢模式匹配,因为案例总是对齐的,使其更具可读性。此外,他们(可以说)更容易扩展,以处理更多案件。我认为模式匹配是if / then / else的演变。

模式匹配也没有额外的运行时成本,无论是否有警卫。

答案 2 :(得分:0)

我对一个自写素数生成器进行了一些测试,据我所知,有&#34;如果那么其他&#34;比模式匹配慢得多,但不能解释为什么,但是我已经测试过F#的imprativ部分,当它达到最优算法时,其运行时间比递归函数式慢。

答案 3 :(得分:0)

同意@Daniel模式匹配通常更灵活。 检查此实现:

type Solution = | Identity | Roots of float list

let quadraticEquation x =

    let rec removeZeros list =
        match list with
        | 0.0::rest -> removeZeros rest
        | _ -> list
    let x = removeZeros x

    match x with
    | [] -> Identity // zero constant
    | [_] -> Roots [] // non-zero constant
    | [a;b] -> Roots [ -b/a ] // linear equation
    | [a;b;c] ->
        let delta = b*b - 4.0*a*c
        match delta with
        | delta when delta < 0.0 -> 
            Roots [] // no real roots
        | _ ->
            let d = sqrt delta
            let x1 = (-b-d) / (2.0*a)
            let x2 = (-b+d) / (2.0*a)
            Roots [x1; x2]
    | _ -> failwithf "equation is bigger than quadratic: %A" x

另请注意https://fsharpforfunandprofit.com/learning-fsharp/不建议使用if-else。它被认为是竞标较少的功能。