Опубликован: 19.09.2008 | Доступ: свободный | Студентов: 658 / 70 | Оценка: 4.50 / 5.00 | Длительность: 21:25:00
Лекция 4:

Выражения

3.13 Case-выражения

exp -> case exp of { alts }
alts -> alt1 ; ... ; altn (n>=1)
alt -> pat -> exp [where decls]
| pat gdpat [where decls]
| (пустая альтернатива)
gdpat -> gd -> exp [ gdpat ]
gd -> | exp0

Перевод:

выражение ->      case выражение of { список-альтернатив
список-альтернатив -> альтернатива1 ; ... ; альтернативаn (n>=1)
альтернатива -> образец -> выражение [where список-объявлений]
| образец образец-со-стражами [where список-объявлений]
| (пустая альтернатива)
образец-со-стражами -> страж -> выражение [ образец-со-стражами ]
страж -> | выражение0

Case-выражение имеет общий вид

case e of { p1 match1 ; ... ; pn matchn }

где каждый matchi имеет общий вид

| gi1-> ei1
...
| gimi-> eimi
where declsi

(Заметьте, что в синтаксическом правиле для gd "|" является терминальным символом, а не синтаксическим мета-символом для указания альтернатив.) Каждая альтернатива pi matchi состоит из образца pi и его сопоставлений matchi. Каждое сопоставление, в свою очередь, состоит из последовательности пар стражей gij и тел eij (выражений), за которыми следуют необязательные связывания (declsi), чья область видимости распространяется над всеми стражами и выражениями альтернативы. Альтернатива вида

pat -> exp where decls

интерпретируется как краткая запись для

pat | True-> exp
where decls

Case-выражение должно иметь по крайней мере одну альтернативу, и каждая альтернатива должна иметь по крайней мере одно тело. Каждое тело должно иметь один и тот же тип, и все выражение должно быть того же типа.

Вычисление case-выражения выполняется посредством сопоставления выражения e отдельным альтернативам. Альтернативы проверяются последовательно, сверху вниз. Если e соответствует образцу в альтернативе, выполняется связывание переменных, сначала указанных в образце, а затем - с помощью declsi в операторе where, связанном с этой альтернативой. Если значение одного из вычисляемых стражей окажется True, в том же окружении, что и страж, будет вычислена соответствующая правая часть. Если значения всех стражей окажутся False, процесс сопоставления с образцом будет возобновлен со следующей альтернативы. Если не удастся сопоставить ни один образец, результатом будет _|_. Сопоставление с образцом описано в разделе "3.17" , а формальная семантика case-выражений - в разделе "3.17.3."

Замечание о разборе. Выражение

case x of { (a,_) | let b = not a in b :: Bool -> a }

нелегко правильно интерпретировать при разборе. Оно имеет единственную однозначную интерпретацию, а именно:

case x of { (a,_) | (let b = not a in b :: Bool) -> a }

Тем не менее, выражение Bool -> a является синтаксически правильным типом, и синтаксические анализаторы с ограниченным предварительным просмотром могут выбрать этот неправильный вариант, и тогда программа будет признана недопустимой. Поэтому мы советуем программистам избегать использования стражей, которые заканчиваются указанием сигнатуры типа, именно поэтому gd содержит exp0, а не не exp.

3.14 Do-выражения

exp -> do { stmts } (do-выражение)
stmts -> stmt1 ... stmtn exp [;] (n>=0)
stmt -> exp ;
| pat >= exp ;
| let decls ;
| ; (пустая инструкция)

Перевод:

выражение -> do { список-инструкций } (do-выражение)
список-инструкций -> инструкция1 ... инструкцияn выражение [;] (n>=0)
инструкция -> выражение ;
| образец \gets выражение ;
| let список-объявлений ;
| ; (пустая инструкция)

Do-выражения предоставляют более удобный синтаксис для монадического программирования. Оно позволяет записать такое выражение

putStr "x: "    >> 
  getLine         >>= \l ->
  return (words l)

в более традиционном виде:

do putStr "x: "
     l <- getLine
     return (words l)

Трансляция:

Для do-выражений выполняются следующие тождества, которые, после удаления пустых stmts, можно использовать в качестве трансляции в ядро:

do {e} = e

do {e;stmts} = e >> do {stmts}

do {p <- e; stmts} = let ok p = do {stmts}

ok _ = fail "..."

in e >>= ok

do {let decls; stmts} = let decls in do {stmts}

Пропуски "..." обозначают генерируемое компилятором сообщение об ошибке, передаваемое функции fail, желательно давая некоторое указание на местоположение ошибки сопоставления с образцом; функции >>, >>= и fail являются операциями в классе Monad, определенными в Prelude; ok является новым идентификатором.

Как показано в трансляции do, переменные, связанные let, имеют полностью полиморфные типы, тогда как те переменные, которые определены с помощью <-, являются связанными лямбда-выражением и поэтому являются мономорфными.