Треки
/
Elm
Elm
/
Вправи
/
Попутник
Попутник

Попутник

Навчальна вправа

Вступ

Записи

Записи - це структури даних, які обʼєднують повʼязану інформацію під мітками. Вони схожі на обʼєкти в JavaScript чи Java або на структури в C чи Rust, але мають кілька важливих відмінностей.

Створення записів

Записи створюються за допомогою фігурних дужок, а їхні елементи розділяються комами.

firefly =
    { name = "Firefly"
    , creator = "Joss Whedon"
    , episodes = 14
    }

Тип запису визначається схожим синтаксисом, лише замість знаків рівності = використовуються двокрапки :.

firefly : { name : String, creator : String, episodes : Int }
firefly =
    { name = "Firefly"
    , creator = "Joss Whedon"
    , episodes = 14
    }

Повторювати ці визначення типів незручно, тому псевдонім типу для спільного визначення - поширений прийом.

type alias TvSeries =
    { name : String
    , creator : String
    , episodes : Int
    }

firefly : TvSeries
firefly =
    { name = "Firefly"
    , creator = "Joss Whedon"
    , episodes = 14
    }

Псевдонім типу також автоматично надає функцію-конструктор для запису. Іноді це дивує, адже функції в Elm мають починатися з малої літери, але конструктори типів - один із винятків.

firefly : TvSeries
firefly = TvSeries "Firefly" "Joss Whedon" 14

Доступ до полів запису

Основний спосіб отримати доступ до значення поля примірника запису - використати точкову нотацію, наприклад firefly.creator. Компілятор Elm також підтримує спеціальні функції доступу, що починаються з крапки ., наприклад .creator, які працюють із будь-яким записом, що має поле з такою назвою. Це другий виняток із правила, що функції мають починатися з малої літери. Насправді функції, визначені користувачем, мають починатися з малої літери, але два щойно розглянуті приклади створює компілятор, а не програміст.

firefly : TvSeries
firefly =
    { name = "Firefly"
    , creator = "Joss Whedon"
    , episodes = 14
    }

firefly.name
    --> "Firefly"

.creator firefly
    --> "Joss Whedon"

Записи також можна деструктуризувати у привʼязках за допомогою зіставлення зі зразком для записів. Деструктуризація працює з будь-яким записом, що має поля з відповідними назвами.

episodesCount : TvSeries -> Int
episodesCount { episodes } =
    episodes

complicatedCopy : TvSeries -> TvSeries
complicatedCopy show =
    let
        { name, creator } = show
    in
    { name = name
    , creator = creator
    , episodes = episodesCount show
    }

Оновлення записів

Записи незмінні, як і все в Elm, тож щойно запис визначено, значення його полів уже не можуть змінитися. Існує спеціальний синтаксис, щоб створити копію наявного запису, але зі зміненими одним або кількома полями. Цей синтаксис використовує символ вертикальної риски |, щоб відрізнити початковий запис від полів, які зміняться, наприклад { oldRecord | field1 = newValue }.

firefly : TvSeries
firefly =
    { name = "Firefly"
    , creator = "Joss Whedon"
    , episodes = 14
    }

updatedFirefly : TvSeries
updatedFirefly =
    { firefly | creator = "J Whedon", episodes = 15 }

Порівняння записів

Elm використовує [структурну рівність][equality], тобто два примірники одного запису з однаковими значеннями вважаються рівними.

firefly1 = TvSeries "Firefly" "Joss Whedon" 14
firefly2 = TvSeries "Firefly" "Joss Whedon" 14

firefly1 == firefly2
    --> True

Розширювані записи

Elm також підтримує структурну типізацію: якщо функції потрібен запис із полями x та y, вона працюватиме з будь-яким записом, що має ці поля, наприклад із двовимірними точками, тривимірними точками, космічними кораблями тощо. Такі типи записів мають у своєму визначенні вертикальну риску |, наприклад { a | x : Float, y : Float }, і в термінології Elm називаються «розширюваними записами». Звернімо увагу на різницю між вертикальною рискою | у визначенні типу, що є визначенням розширюваного запису ({ a | x : Int }), та вертикальною рискою | у самому примірнику запису, яка означає, що ми оновлюємо деякі поля цього запису.

point2d = { x = 1, y = 2 }
point3d = { x = 3, y = 4, z = 7 }

.x point2d --> 1
.x point3d --> 3

length : { a | x : Float, y : Float } -> Float
length vector =
    sqrt (vector.x * vector.x + vector.y * vector.y)

length point2d --> 2.236068
length point3d --> 5

translateX : { a | x : Float } -> { a | x : Float }
translateX vector =
    { vector | x = vector.x + 1 }

translateX point2d --> { x = 2, y = 2 }
translateX point3d --> { x = 4, y = 4, z = 7 }

Вказівки

У цій вправі ми - великі фанати спорту, які щойно відкрили для себе пристрасть до баскетболу НБА. Оскільки ми новачки в баскетболі НБА, ми глибоко занурюємося в історію НБА: відстежуємо команди, тренерів, їхню статистику перемог і поразок та порівнюємо їх між собою.

Оскільки в нас ще немає улюбленої команди, ми також розробимо алгоритм, який допоможе визначити, чи варто вболівати за ту чи іншу команду. На нас чекає сім завдань, які допоможуть розробити власний алгоритм уболівання за команду.

1. Визначте модель

Визначте псевдонім типу Coach для запису з двома такими полями, у такому порядку:

  • name: імʼя тренера, типу String.
  • formerPlayer: показує, чи був тренер колишнім гравцем, типу Bool.

Визначте псевдонім типу Stats для запису з двома такими полями, у такому порядку:

  • wins: кількість перемог, типу Int.
  • losses: кількість поразок, типу Int.

Визначте псевдонім типу Team для запису з трьома такими полями, у такому порядку:

  • name: назва команди, типу String.
  • coach: тренер команди, типу Coach.
  • stats: статистика команди, типу Stats.

2. Створіть команду

Нам потрібно взаємодіяти зі стороннім кодом, у якому є поняття команди, але який викликає наш код із параметрами в іншому порядку. Додайте функцію createTeam із такою сигнатурою String -> Stats -> Coach -> Team.

3. Замініть тренера

Власники клубів НБА нетерплячі, тож ми помітили, що погані результати команди часто призводять до заміни тренера. Реалізуйте функцію replaceCoach, яка приймає нового тренера й команду як параметри та повертає команду, але з новим тренером:

let
    coach = { name = "Larry Bird", formerPlayer = True }
    stats = { wins = 58, losses =  24 }
    team = { name = "Indiana Pacers", coach = coach, stats =  stats }
    newCoach = { name = "Isiah Thomas", formerPlayer = True }
in
replaceCoach newCoach team
--> { name = "Indiana Pacers"
--  , coach = { name = "Isiah Thomas", formerPlayer = True }
--  , stats = { wins = 58, losses = 24 } }

4. Перевірте, чи варто вболівати за команду

У майбутньому ми плануємо створити складний алгоритм, який визначатиме, чи варто вболівати за команду, але для першої спроби ми вирішили вболівати за будь-яку команду, у якої більше перемог, ніж поразок.

Ми хочемо, щоб цю функцію можна було повторно використовувати в інших контекстах, тож використайте розширюваний запис у сигнатурі типу, щоб приймати будь-який запис із відповідними полями. Щоб функція була якомога лаконічнішою, скористайтеся зіставленням зі зразком, щоб дістати stats із параметра функції. Реалізуйте функцію rootForTeam, яка приймає команду та повертає True, якщо за цю команду варто вболівати; інакше повертає False.

let
    spursCoach = { name = "Gregg Popovich", formerPlayer =  False }
    spursStats = { wins = 56, losses = 26 }
    spursTeam = { name = "San Antonio Spurs", coach = spursCoach, stats = spursStats }
in
rootForTeam spursTeam
--> True
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці
Elm Exercism

Час розпочати Попутник?

Зареєструйтеся на Exercism, щоб вивчати й опановувати Elm, а також 28 концепцій110 вправ та справжнє наставництво від людей, і все це безкоштовно.