Сопоставление с образцом с защитой против конструкции if / else в F # - PullRequest
7 голосов
/ 03 ноября 2011

В языках семейства 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 ]

9 голосов
/ 03 ноября 2011

У обоих свое место.Люди больше привыкли к конструкции If / else для проверки значения, где сопоставление с образцом похоже на If / else на стероидах.Сопоставление с образцом позволяет сортировать сравнение со структурой данных decomposed вместе с использованием gaurds для указания некоторых дополнительных условий для частей разложенных данных или какого-либо другого значения (особенно в случае рекурсивных структур данных или так называемых дискриминационных объединений).в F #).

Лично я предпочитаю использовать if / else для простых сравнений значений (true / false, int и т. д.), но в случае, если у вас есть рекурсивная структура данных или что-то, что вам нужно сравнить с разложеннымзначение, чем нет ничего лучше, чем сопоставление с шаблоном.

Сначала сделайте так, чтобы это работало, и сделали его элегантным и простым, а затем, если вам кажется, что есть проблемы с производительностью, проверьте наличие проблем с производительностью (что в основном будет связано с некоторой другой логикой ине из-за сопоставления с образцом)

7 голосов
/ 03 ноября 2011

Правильный ответ, вероятно, , это зависит , но я предполагаю, что в большинстве случаев скомпилированное представление одинаково.Например,

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.

Кроме того, не требуется дополнительная стоимость выполнения сопоставления с образцом, с защитой или без нее.

0 голосов
/ 09 апреля 2018

Согласитесь с @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. Считается, что предложение менее функционально.

0 голосов
/ 04 марта 2017

Я провел некоторое тестирование на самописанном генераторе простых чисел, и, насколько я могу сказать, что «если тогда еще» значительно медленнее, чем сопоставление с образцом, хотя объяснить не могу, почему, но я, насколько я могу, протестированные императивные части F # имеют более медленное время выполнения, чем рекурсивный функциональный стиль, когда дело доходит до оптимальных алгоритмов.

...