Изучай Haskell во имя добра! (Липовача) - страница 37

>cylinder :: Double -> Double -> Double

>cylinder r h =

>  let sideArea = 2 * pi * r * h

>    topArea = pi * r    2

>  in sideArea + 2 * topArea

Общее выражение выглядит так: >let <определения> in <выражение>. Имена, которые вы определили в части >let, видимы в выражении после ключевого слова >in. Как видите, мы могли бы воспользоваться ключевым словом >where для той же цели. Обратите внимание, что имена также выровнены по одной вертикальной позиции. Ну и какая разница между определениями в секциях >where и >let? Просто, похоже, в секции >let сначала следуют определения, а затем выражение, а в секции >where – наоборот.

На самом деле различие в том, что определения >let сами по себе являются выражениями. Определения в секциях >where – просто синтаксические конструкции. Если нечто является выражением, то у него есть значение. >"Фуу!" – это выражение, и >3+5 – выражение, и даже >head [1,2,3]. Это означает, что определение >let можно использовать практически где угодно, например:

>ghci> 4 * (let a = 9 in a + 1) + 2

>42

Ключевое слово >let подойдёт для определения локальных функций:

>ghci> [let square x = x * x in (square 5, square 3, square 2)]

>[(25,9,4)]

Если нам надо привязать значения к нескольким переменным в одной строке, мы не можем записать их в столбик. Поэтому мы разделяем их точкой с запятой.

>ghci> (let a = 10; b = 20 in a*b, let foo="Эй, "; bar = "там!" in foo ++ bar)

>(200,"Эй, там!")

Как мы уже говорили ранее, определения в секции >let могут использоваться при сопоставлении с образцом. Они очень полезны, к примеру, для того, чтобы быстро разобрать кортеж на элементы и привязать значения элементов к переменным, а также в других подобных случаях.

>ghci> (let (a,b,c) = (1,2,3) in a+b+c) * 100

>600

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

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

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

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

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

Мы поместили выражение >let в генератор списка так, словно это предикат, но он не фильтрует список, а просто определяет имя. Имена, определённые в секции