Шаблоны функций

Завершено

Вы взяли часто используемые выражения и превратили их в функции. Теперь ваш код выглядит лучше и читать его стало легче. Когда вы приобретете достаточный опыт использования функций, настанет время обратить внимание на существующие эффективные шаблоны. Используя эти шаблоны, вы получаете код, который проще читать и поддерживать.

Сравнение декларативного и императивного подходов

Работу над кодом, вы, скорее всего, начинаете с записи выражения. Затем вы пишете еще одно выражение, потом другое и т. д. Вам нужно решить проблему, и вы подбираете конкретное решение. Этот подход называется императивным подходом. В нем ничего плохого нет, поскольку он решает возникшую проблему. Но есть еще один маршрут, который является декларативным подходом. Пример декларативного подхода — это запрос к базе данных с помощью SQL.

В нем используются выражения, аналогичные следующему:

SELECT * 
FROM Students s
WHERE s.Location = "Ohio" 

Что делает этот код декларативным, заключается в том, что вы просите о том, что хотите, но вы не указываете , как вы хотите решить проблему. Вы оставляете как для SQL.

Этот подход также можно применить к F#. В следующем коде используется декларативный подход:

let studentsFromOhio = 
    allStudents  
    |> filterLocation "Ohio"

В представленном выше коде вы работаете с данными и указываете, что вам нужно, не уточняя, как это должно быть сделано. Когда ваш код выглядит так, как в приведенном выше примере, его легко читать и понимать. Чтобы этого добиться, рассмотрим некоторые полезные шаблоны, поддерживаемые в F#.

Функциональные шаблоны

В F# есть некоторые полезные шаблоны, которые можно использовать для более функционального подхода. Рассмотрим следующие из них.

  • Композиция: композиция объединяет несколько функций в одну функцию.
  • Конвейер: конвейер начинается со значения, а затем последовательно вызывает несколько функций, используя выходные данные одной функции в качестве входных данных для следующей функции.

Создание

Композиция — это объединение функций и применение их друг за другом в определенном порядке. Оператор композиции применяется к двум функциям и возвращает новую функцию.

Когда вы пишете код, вам часто приходится вызывать одну функцию, а сразу после нее другую. Например, вы хотите упорядочить список и отфильтровать все продукты со скидкой.

В следующем примере вызывается функция add2(), а ее результат передается в функцию multiply3().

let add2 a = a + 2
let multiply3 a = a * 3 
let addAndMultiply a =
    let sum = add2 a
    let product = multiply3 sum
    product

printfn "%i" (addAndMultiply 2) // 12

Этот шаблон настолько распространен, что в языке F# есть отдельный оператор для него. Оператор >> позволяет объединить несколько функций в одну более крупную функцию. Оператор >> можно использовать для упрощения приведенного выше кода, например следующим образом:

let add2 a = a + 2
let multiply3 a = a * 3 
let addAndMultiply = add2 >> multiply3

printfn "%i" (addAndMultiply 2) // 12

Объединенная функция addAndMultiply() применяет функции, которые она включает, слева направо. В этом примере сначала выполняется функция add2(), а затем multiply3().

Трубопровод

Оператор конвейера |> получает функцию и аргумент и возвращает значение. Давайте на следующем примере рассмотрим, чем конвейер отличается от композиции:

let list = [4; 3; 1]
let sort (list: int list) = List.sort list
let print (list: int list)= List.iter(fun x-> printfn "item %i" x) list

list |> sort |> print // item 1 item 3 item 4

В последней строке кода вы начинаете со списка целых чисел, используя list, который служит входными данными для первой функции sort(). Результат этой операции передается в print(). Главное отличие между конвейером и композицией заключается в том, что в конвейере сначала указываются данные (в данном случае список целых числе), а затем набор функций.