Условное выражение
Prelude> let f x = if x > 0 then 1 else (-1) Prelude> f 5 1 Prelude> f (-5) -1
В Haskell отрицательные числа получаются с помощью знака - перед соответствующим литералом и во многих случаях их следует заключать в скобки. Если бы мы использовали синтаксис без скобок, то получили бы выражение f - 5.
Обе ветви then и else должны присутствовать. В ветвях then и else должны стоять выражения одного и того же типа, иначе GHCi вернет сообщение об ошибке, потому что функция определена неверно.
Условное выражение можно использовать в построении более сложных выражений.
Prelude> let g x = (if x > 0 then 1 else (-1)) + 3 Prelude> g 5 4 Prelude> g (-7) 2
Функция, которая возвращает 1, если ей передано положительное число, (-1), если отрицательное, и 0 в случае, когда передан 0:
sign x = if x > 0 then 1 else if x == 0 then 0 else (-1)
Сопоставление с образцом
Использование условного выражения if then else не всегда удобно. В Haskell существует гораздо более мощный инструмент решающий ту же задачу. Это так называемое сопоставление с образцом. Основная идея заключается в том что мы определяем функцию не с помощью одного уравнения, а с помощью нескольких уравнений. Каждое из этих уравнений описывает одну из возможных ветвей программы.factorial' 0 = 1 factorial' n = n * factorial' (n - 1)
Prelude> let {factorial' 0 = 1; factorial' n = n * factorial' (n - 1)} Prelude> factorial' 0 1
Следующие функции, могут привести к расходимости (мы называем выражение расходящимся, если вычисление его значения приводит к бесконечному циклу или аварийному завершению):
grault x 0 = x grault x y = x garply = grault 'q'
Охранные выражения
Сопоставление с образцом не всегда идеально подходит для описания подходящего ветвления. Если условие на основании которого осуществляется ветвление представляет собой большое выражение, то довольно трудно осуществить описание этого выражения на языке сопоставления с образцом. Для этого случая в Haskell есть другой инструмент, который зовется "охранные выражения".Охранные выражения позволяют расщепить одно уравнение на несколько. Охранное выражение представляет собой булево выражение, которое отделяется от параметров функции вертикальной чертой. Это булево выражение проверяется, оно может зависеть от формальных параметров функции. При вызове функции, если сопоставление с образцом произошло удачно, то происходит проверка охранных выражений. Если охранное выражение обращается в истину, то тогда выполняется правая часть, а если охранное выражение ложно, то происходит переход к следующему охранному выражению (их может быть два или больше) и тоже проверяется истинность указанного логического выражения и если оно истинно, то тоже происходит переход в правую часть. Если же все охранные выражения являются ложными, то происходит переход к следующей строчке сопоставления с образцом (если таковая присутствует). Если же следующего образца нет, то программа аварийно прерывается сообщением об ошибке о том, что сопоставление с образцом является не полным.
factorial''' 0 = 1 factorial''' n | n < 0 = error "arg must be >= 0" | n > 0 = n * factorial''' (n - 1) factorial4 :: Integer -> Integer factorial4 n | n == 0 = 1 | n > 0 = n * factorial4 (n - 1) | otherwise = error "arg must be >= 0"
Идентификатор otherwise. Это не ключевое слово, а константа, определенная для удобства в стандартной библиотеке:
Prelude> otherwise True
Выражение let in
Конструкция let in позволяет ликвидировать повторяющиеся выражения.Prelude> let x = True in (True,x) (True,True) Prelude> (let x = 'w' in [x,'o',x]) ++ "!" "wow!"
roots' a b c = let d = sqrt (b ^ 2 - 4 * a * c) in ((-b - d) / (2 * a), (-b + d) / (2 * a))
Выражение let in может задавать сразу несколько связываний.
roots'' a b c = let {d = sqrt (b ^ 2 - 4 * a * c); x1 = (-b - d) / (2 * a); x2 = (-b + d) / (2 * a)} in (x1, x2)
Можно организовывать связываемые выражения с помощью отступов.
roots''' a b c = let x1 = (-b - d) / aTwice x2 = (-b + d) / aTwice d = sqrt $ b ^ 2 - 4 * a * c aTwice = 2 * a in (x1, x2)
С помощью выражения let in можно также определять локальные функции.
factorial6 n | n >= 0 = let helper acc 0 = acc helper acc n = helper (acc * n) (n - 1) in helper 1 n | otherwise = error "arg must be >= 0"
Возможно также не только локальное определение функции, но и локальное связывание образцов.
rootsDiff a b c = let (x1,x2) = roots a b c in x2 - x1
Попытка вычислить следующее выражение приводит к бесконечному циклу.
quux = let x = x in x
Конструкция where
Еще одна конструкция позволяющая обеспечивать локальные связывания. Конструкция where очень похожа на выражение let in, но устроена ровно наоборот. В выражении let in часть в которой находится локальное связывание стоит сначала, а потом фигурирует выражение в котором это локальное связывание используется. В конструкции where сначала идет выражение в котором используются какие-то переменные, а потом внутри выражение where происходит локальное связывание.roots'''' a b c = (x1, x2) where x1 = (-b - d) / aTwice x2 = (-b + d) / aTwice d = sqrt $ b ^ 2 - 4 * a * c aTwice = 2 * a
Между конструкцией where и выражением let in есть одно отличие. Конструкция let in является выражением.
Prelude> let x = 2 in x^2 4 Prelude> (let x = 2 in x^2)^2 16
factorial7 :: Integer -> Integer factorial7 n | n >= 0 = helper 1 n | otherwise = error "arg must be >= 0" where helper acc 0 = acc helper acc n = helper (acc * n) (n - 1)
Комментариев нет:
Отправить комментарий