when и условия паттернов

Последнее обновление: 04.03.2024

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

match выражение with
| шаблон_1 [ when ограничение ] -> действия_1
| шаблон_2 [ when ограничение ] -> действие_2

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

let checkAge person =
    match person with
    | (_, age) when age > 110 || age < 1 -> "Недействительный возраст"  // если age больше 110 и меньше 1
    | (_, age) when age >=1 && age<18 -> "Доступ запрещен"           // если возраст равен или больше 1 и меньше 18
    | _ -> "Доступ разрешен"                      // в остальных случаях

printfn "%s" (checkAge ("Tom", 39))         // Доступ разрешен
printfn "%s" (checkAge ("Bob", 100500))     // Недействительный возраст
printfn "%s" (checkAge ("Sam", 16))         // Доступ запрещен

Допустим, извне передаются данные пользователя в виде кортежа с именем и возрастом, и в зависимости от возраста нам надо разрешить или запретить доступ к некоторым ресурсам. С помощью паттерна кортежей мы можем получить данные, в частности, возраст в переменную. А с помощью оператора when вводим ограничение для значения возраста. Так, следующий паттерн сопоставляется с кортежем, если второй элемент кортежа больше 110 или меньше 1:

| (_, age) when age > 110 || age < 1 -> "Недействительный возраст"

Аналогично работают другие шаблоны.

Другой пример. По умолчанию F# не предоставляет встроенной функции по удалению элемента из списка. Но мы можем написать свою:

let mutable numbers = [1; 2; 3; 4; 5]

let rec remove lst value = 
    match lst with
    | head :: tail when head=value -> tail
    | head :: tail -> head :: (remove tail value)
    | _ -> []

// удаляем число 3
numbers <- remove numbers 3

printfn "%A" numbers    //  [1; 2; 4; 5]

Здесь определена рекурсивная функция remove, которая принимает список (lst) и удаляемое значение (value). С помощью pattern matching раскладываем список на первый элемент и список последующих элементов

head :: tail

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

| head :: tail when head=value -> tail

Второй шаблон применяется, если первый элемент (head) НЕ равен value. В этом случае возвращаем список, который состоит из первого элемента и результата применения функции remove к списку tail:

| head :: tail -> head :: (remove tail value)

Если в списке нет элементов, то возвращаем пустой список

| _ -> []

Другой более сложный пример с проверкой свойств:

type Person(name: string) = 
    member this.Name = name

type Employee(name: string, company: string) = 
    inherit Person(name)
    member this.Company = company

let printPerson (p: Person) =
    match p with
    | :? Employee as emp when emp.Company = "LocalComp" -> printfn "%s работает в %s" emp.Name emp.Company
    | :? Employee as emp -> printfn "%s где-то работает" emp.Name
    | _ -> printfn "%s не работает" p.Name

let tom = Person("Tom")
let bob = Employee("Bob", "LocalComp")
let sam = Employee("Sam", "GlobalComp")

printPerson tom     // Tom не работает
printPerson bob     // Bob работает в LocalComp
printPerson sam     // Sam где-то работает

Здесь у нас есть класс человека - класс Person и класс-наследник Employee, который представляет работника. В функции printPerson получаем объект Person и смотрим, какой он тип представляет. Если он представляет тип Employee и его свойство Company равно "LocalComp", то значение сопоставляется со следующим шаблоном:

 :? Employee as emp when emp.Company = "LocalComp" -> printfn "%s работает в %s" emp.Name emp.Company

Для проверки компании здесь вводится ограничение when emp.Company = "LocalComp". Это позволяет разграничить работников определенной компании от остальных работников.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850