Я думаю, вы найдете наиболее продуктивные языки, позволяющие смешивать функциональные и императивные стили, такие как OCaml и F #.
В большинстве случаев я могу написать код, который представляет собой просто длинную строку «map x to y, уменьшите y до z». В 95% случаев функциональное программирование упрощает мой код, но есть одна область, где неизменность проявляет себя:
Широкое несоответствие между простотой реализации и неизменяемым стеком и неизменной очередью.
Стеки легки и хорошо сочетаются с настойчивостью, очереди смешны.
Наиболее распространенные реализации неизменяемых очередей используют один или несколько внутренних стеков и ротацию стеков. Преимущество состоит в том, что эти очереди работают в O (1) большую часть времени , но некоторые операции будут выполняться в O (n). Если вы полагаетесь на постоянство в своем приложении, то в принципе возможно, что каждая операция выполняется в O (n). Эти очереди бесполезны, когда вам нужна производительность в реальном времени (или, по крайней мере, постоянная).
Крис Окасаки предоставляет реализацию неизменяемых очередей в его книге , они используют лень для достижения O (1) для всех операций. Это очень умная, разумно сжатая реализация очереди в реальном времени, но она требует глубокого понимания деталей, лежащих в ее основе, и все еще на порядок сложнее, чем неизменный стек.
В отличие от этого, я могу написать стек и очередь, используя изменяемые связанные списки, которые выполняются в постоянное время для всех операций, и полученный код будет очень простым.
Что касается площади многоугольника, ее легко преобразовать в функциональную форму. Давайте предположим, что у нас есть векторный модуль, подобный этому:
module Vector =
type point =
{ x : float; y : float}
with
static member ( + ) ((p1 : point), (p2 : point)) =
{ x = p1.x + p2.x;
y = p1.y + p2.y;}
static member ( * ) ((p : point), (scalar : float)) =
{ x = p.x * scalar;
y = p.y * scalar;}
static member ( - ) ((p1 : point), (p2 : point)) =
{ x = p1.x - p2.x;
y = p1.y - p2.y;}
let empty = { x = 0.; y = 0.;}
let to_tuple2 (p : point) = (p.x, p.y)
let from_tuple2 (x, y) = { x = x; y = y;}
let crossproduct (p1 : point) (p2 : point) =
{ x = p1.x * p2.y; y = -p1.y * p2.x }
Мы можем определить нашу функцию площади, используя немного магии кортежей:
let area (figure : point list) =
figure
|> Seq.map to_tuple2
|> Seq.fold
(fun (sum, (a, b)) (c, d) -> (sum + a*d - b*c, (c, d) ) )
(0., to_tuple2 (List.hd figure))
|> fun (sum, _) -> abs(sum) / 2.0
Или вместо этого мы можем использовать кросс-произведение
let area2 (figure : point list) =
figure
|> Seq.fold
(fun (acc, prev) cur -> (acc + (crossproduct prev cur), cur))
(empty, List.hd figure)
|> fun (acc, _) -> abs(acc.x + acc.y) / 2.0
Я не нахожу ни одну из функций нечитабельными.