Сопоставление с шаблоном с защитой против конструкции if/else в F#
В языках семейства 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 ответа
Правильный ответ, вероятно, зависит, но я предполагаю, что в большинстве случаев скомпилированное представление одинаково. В качестве примера
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
,
Также нет дополнительных затрат времени выполнения для сопоставления с образцом, с защитой или без нее.
Оба имеют свое место. Люди более привыкли к конструкции If/else для проверки значения, где сопоставление с образцом похоже на If/else на стероидах. Сопоставление с образцом позволяет вам сравнивать decomposed
структура данных вместе с использованием gaurds для указания некоторых дополнительных условий на части разложенных данных или некоторого другого значения (особенно в случае рекурсивных структур данных или так называемых различающихся объединений в F#).
Лично я предпочитаю использовать if/else для простых сравнений значений (true/false, ints и т. Д.), Но в случае, если у вас есть рекурсивная структура данных или что-то, что вам нужно сравнить с разложенным значением, нет ничего лучше, чем сопоставление с образцом.
Сначала заставьте его работать, сделайте его элегантным и простым, а затем, если вам покажется, что у вас проблемы с производительностью, проверьте наличие проблем с производительностью (что в основном будет связано с некоторой другой логикой, а не с сопоставлением с шаблоном).
Согласитесь с @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. Это считается ставкой менее функциональным.
Я провел некоторое тестирование на самописанном генераторе простых чисел, и, насколько я могу сказать, "если тогда еще" значительно медленнее, чем сопоставление с образцом, я не могу объяснить почему, но я постарался проверить императив. часть F# имеет более медленное время выполнения, чем рекурсивный функциональный стиль, когда дело доходит до оптимальных алгоритмов.