Зіставлення зі зразками уможливлює виразний розгалужений код, а деструктуризація дає елегантне звʼязування значень зі змінними.
Зіставлення зі зразками може звʼязувати значення зі змінними або відкидати їх за допомогою символу-замінника _, використовуючи конструкцію case.
type Entity = Alien | Stranger (Maybe String) | Friend { name: String }
hello : Entity -> String
hello entity =
case entity of
-- Custom type variant
Alien -> "Hello, you are not from around here are you?"
-- Binding to a litteral value nested in Stranger
Stranger (Just nametag) -> "Hello, erm... " ++ nametag ++ "."
-- Discarding Friend's data
Friend _ -> "Hi!"
-- All other cases
_ -> "Hello stranger!"
Деструктуризація може звʼязувати значення в привʼязках let, аргументах функцій і, звісно, у виразах case, коли для даних можлива лише одна форма.
pairSum : ( Int, Int ) -> Int
pairSum pair =
-- Destructuring of a pair in a 'let' binding
let ( x, y ) = pair
in x + y
-- Destructuring in the function argument
pairSum : ( Int, Int ) -> Int
pairSum ( x, y ) = x + y
-- Custom type containing a single variant
type Container = Box String
-- Destructuring in the function argument
unbox : Container -> String
unbox (Box str) = str
-- Destructuring combined with pattern matching
unboxMaybe : Maybe Container -> Maybe String
unboxMaybe maybeContainer =
case maybeContainer of
Nothing -> Nothing
Just (Box "42") -> Just "The answer to the universe!"
Just (Box str) -> Just str
Деструктуризацію також можна застосовувати до записів або разом із ключовим словом as.
type alias Circle =
{ radius : Float
, center : ( Float, Float )
}
perimeter : Circle -> Float
perimeter { radius } =
2 * pi * radius
left : Circle -> Float
left { radius, center } =
-- the pair center cannot be pattern matched directly in the function argument
-- so we do it in a 'let' statement
let ( x, _ ) = center
in x - radius
-- using the 'as' keyword to bind both the fields and the whole record
smaller : Float -> Circle -> Circle
smaller reduction ({ radius } as circle) =
if reduction < radius then
{ circle | radius = radius - reduction }
else
circle
Ми працюємо в IT-службі підтримки й хочемо вдосконалити систему тікетів, щоб вона працювала ефективніше та давала статистику для нашого керівника.
Тікет має статус (new, in progress...), автора (його визначають за іменем користувача та ідентифікатором співробітника), його можна призначити IT-працівнику, і він містить історію коментарів.
Ось чотири завдання, які допоможуть удосконалити нашу поточну систему тікетів.
Порожні коментарі захаращують інтерфейс і не несуть жодної інформації, тож ми хочемо їх відфільтрувати.
Визначмо допоміжну функцію emptyComment, яка приймає пару «користувач/коментар» і повертає True для порожніх коментарів.
Використаймо деструктуризацію, щоб одразу дістати користувача й коментар із аргументу функції. Користувача можна проігнорувати, бо ця інформація нам не потрібна.
emptyComment ( User "Alice", "" )
-- => True
Наш керівник хоче мати статистику залученості користувачів до роботи з тікетами. Вона просить нас перевірити, скільки коментарів додає автор тікета.
Визначмо numberOfCreatorComments, яка приймає тікет і повертає кількість коментарів, доданих автором тікета.
Використаймо деструктуризацію в аргументі функції, щоб одночасно дістати запис із типу Ticket і потрібні нам поля цього запису.
Потім деструктуруймо createdBy у звʼязуванні let, щоб порахувати відповідні коментарі.
Використаймо функції з модуля List для підрахунку, а в аргументі анонімної функції застосуймо деструктуризацію, щоб розділити пару «користувач/коментар».
numberOfCreatorComments
numberOfCreatorComments
(Ticket
{ status = Closed
, createdBy = ( User "Chiaki", 1 )
, assignedTo = Nothing
, comments = [ ( User "Chiaki", "Never mind, fixed it." ) ]
}
)
-- => 1
Деякі тікети потрібно ескалювати до Аліси, Боба або Чарлі з команди розробників. Важливо стежити за ними, щоб переконатися, що робота просувається.
Визначмо допоміжну функцію assignedToDevTeam, яка перевіряє, чи призначено тікет одному з членів команди розробників.
Використаймо деструктуризацію в аргументі функції, щоб дістати запис із типу Ticket і потрібне нам поле.
Потім скористаймося однією конструкцією case, щоб перевірити, чи призначено тікет Алісі, Бобу або Чарлі: рекурсивне зіставлення зі зразком дістане їхні імена користувачів, а літеральне зіставлення зі зразком їх збіжить.
assignedToDevTeam
(Ticket
{ status = InProgress
, createdBy = ( User "Bill", 2 )
, assignedTo = Just (User "Alice")
, comments = [ ( User "Bill", "What's an 'undefined'?" ) ]
}
)
-- => True
Іноді купу тікетів потрібно перепризначити іншим IT-працівникам.
Визначмо функцію assignTicketTo, яка допоможе це зробити. Функція приймає два аргументи: користувача, якому призначають тікет, і сам тікет.
Якщо статус тікета New, призначмо його новому користувачу й змінімо статус на InProgress.
Для інших статусів просто перепризначмо тікет новому користувачу, крім тікетів зі статусом Archived, які не можна змінювати взагалі.
Використаймо деструктуризацію в аргументі функції, щоб дістати запис із типу Ticket і потрібне нам поле, а також скористаймося ключовим словом as, щоб зберегти весь запис таким, як він є.
Скористаймося однією конструкцією case, щоб зіставити статус зі зразком і повернути відповідний тікет, створений зі зміненого оригінального запису, який ми зберегли за допомогою ключового слова as.
assignTicketTo (User "Danny")
(Ticket
{ status = New
, createdBy = ( User "Jesse", 3 )
, assignedTo = Just (User "Alice")
, comments = [ ( User "Jesse", "I've been waiting for 6 months!!" ) ]
}
)
-- => Ticket
-- { status = InProgress
-- , createdBy = ( User "Jesse", 3 )
-- , assignedTo = Just (User "Danny")
-- , comments = [ ( User "Jesse", "I've been waiting for 6 months!!" ) ]
-- }
Зареєструйтеся на Exercism, щоб вивчати й опановувати Elm, а також 28 концепцій110 вправ та справжнє наставництво від людей, і все це безкоштовно.