Если определения let настолько хороши, то почему бы только их всё время и не использовать? Ну, так как это всего лишь выражения, причём с локальной областью видимости, то их нельзя использовать в разных охранных выражениях. К тому же некоторые предпочитают, чтобы их переменные вычислялись после использования в теле функции, а не до того. Это позволяет сблизить тело функции с её именем и типом, что способствует большей читабельности.

<p>Выражения let в генераторах списков</p>

Давайте перепишем наш предыдущий пример, который обрабатывал списки пар вида (вес, рост), чтобы он использовал секцию let в выражении вместо того, чтобы определять вспомогательную функцию в секции where.

calcBmis :: [(Double, Double)] -> [Double]

calcBmis xs = [bmi | (w, h) <– xs, let bmi = w / h 2]

Мы поместили выражение let в генератор списка так, словно это предикат, но он не фильтрует список, а просто определяет имя. Имена, определённые в секции let внутри генератора списка, видны в функции вывода (часть до символа |) и для всех предикатов и секций, которые следуют после ключевого слова let. Так что мы можем написать функцию, которая выводит только толстяков:

calcBmis :: [(Double, Double)] -> [Double]

calcBmis xs = [bmi | (w, h) <– xs, let bmi = w / h ^ 2, bmi > 25.0]

Использовать имя bmi в части (w, h) <– xs нельзя, потому что она расположена до ключевого слова let.

<p>Выражения let в GHCi</p>

Часть in также может быть пропущена при определении функций и констант напрямую в GHCi. В этом случае имена будут видимы во время одного сеанса работы GHCi.

ghci> let zoot x y z = x * y + z

ghci> zoot 3 9 2

29

ghci> let boot x y z = x * y + z in boot 3 4 2

14

ghci> boot

:1:0: Not in scope: `boot'

Поскольку в первой строке мы опустили часть in, GHCi знает, что в этой строке zoot не используется, поэтому запомнит его до конца сеанса. Однако во втором выражении let часть in присутствует, и определённая в нём функция boot тут же вызывается. Выражение let, в котором сохранена часть in, является выражением и представляет некоторое значение, так что GHCi именно это значение и печатает.

<p>Выражения для выбора из вариантов</p>

Во многих императивных языках (C, C++, Java, и т. д.) имеется оператор case, и если вам доводилось программировать на них, вы знаете, что это такое. Вы берёте переменную и выполняете некую часть кода для каждого значения этой переменной – ну и, возможно, используете финальное условие, которое срабатывает, если не отработали другие.

Язык Haskell позаимствовал эту концепцию и усовершенствовал её. Само имя «выражения для выбора» указывает на то, что они являются… э-э-э… выражениями, так же как ifthenelse и let. Мы не только можем вычислять выражения, основываясь на возможных значениях переменной, но и производить сопоставление с образцом.

Итак, берём переменную, выполняем сопоставление с образцом, выполняем участок кода в зависимости от полученного значения… где-то мы это уже слышали!.. Ах да, сопоставление с образцом по параметрам при объявлении функции! На самом деле это всего лишь навсего облегчённая запись для выражений выбора. Эти два фрагмента кода делают одно и то же – они взаимозаменяемы:

head' :: [a] –> a

head' [] = error "Никаких head для пустых списков!"

head' (x:_) = x

head' :: [a] –> a

head' xs =

  case xs of

   [] –> error "Никаких head для пустых списков!"

   (x:_) –> x

Как видите, синтаксис для выражений отбора довольно прост:

case expression of

  pattern –> result

  pattern –> result

  ...

Выражения проверяются на соответствие образцам. Сопоставление с образцом работает как обычно: используется первый образец, который подошёл. Если были опробованы все образцы и ни один не подошёл, генерируется ошибка времени выполнения.

Сопоставление с образцом по параметрам функции может быть сделано только при объявлении функции; выражения отбора могут использоваться практически везде. Например:

describeList :: [a] –> String

describeList xs = "Список " ++

  case xs of

   [] –> "пуст."

   [x] –> "одноэлементный."

   xs –> "длинный."

Они удобны для сопоставления с каким-нибудь образцом в середине выражения. Поскольку сопоставление с образцом при объявлении функции – это всего лишь упрощённая запись выражений отбора, мы могли бы определить функцию таким образом:

Перейти на страницу:

Похожие книги