Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ФП_ЛР №3-4.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
2 Mб
Скачать

Операция выбора и правила выравнивания

Ранее был рассмотрен условный оператор. Его естественным продолжением является оператор выбора case, аналогичный конструкции switch языка Си.

Предположим, нам надо определить некоторую (довольно странную) функцию, которая возвращает 1, если ей передан аргумент 0; 5, если аргумент был равен 1; 2, если аргумент равен 2 и -1 во всех остальных случаях. В принципе эту функцию можно записать с помощью операторов if, однако результат будет длинным и малопонятным. В таких случаях помогает использованиеcase:

f x = case x of

0 -> 1

1 -> 5

2 -> 2

_ -> -1

Синтаксис оператора case очевиден из приведенного примера; следует только сделать замечание, что символ _ аналогичен конструкции default в языке Си. Однако у внимательного читателя может возникнуть закономерный вопрос: каким образом интерпретатор языка Haskell распознает, где закончилось определение одного случая и началось определение другого?

Ответ заключается в том, что в языке Haskell используется двумерная система структурирования текста. Эта система позволяет обойтись без специальных символов группировки и разделения операторов, подобным символам {, } и ; языка Си.

Вышеприведенную функцию можно записать и таким образом (демонстрирующем, как не надо оформлять тексты программ):

f x = case x of

{ 0 -> 1; 1 -> 5;

2 -> 2;

_ -> -1 }

Такой способ явно задает группировку и разделение конструкций языка. Однако, можно обойтись и без него.

Общее правило таково, что после ключевых слов where, let, do и of интерпретатор вставляет открывающую скобку ({) и запоминает колонку, в которой записана следующая команда. В дальнейшем перед каждой новой строкой, выровненной на запомненную величину, вставляется разделяющий символ ‘;’. Если следующая строка выровнена меньше (т.е. ее первый символ находится левее запомненной позиции), вставляется закрывающая скобка. Это может выглядеть несколько сложновато, но в действительности все довольно просто.

Применяя описанное правило к определению функции f, получим, что оно воспринимается интерпретатором следующим образом:

f x = case x of {

;0 -> 1

;1 -> 5

;2 -> 2

;_ -> -1

}

В любом случае можно не использовать этот механизм и всегда явно указывать символы {, } и ;. Однако, помимо экономии на количестве нажатий клавиш, применение описанного правила приводит к тому, что получаемые программы более «читабельны».

Сопоставление с образцом

На примере уже рассмотренных нами функций можно продемонстрировать один из важных аспектов Haskell'a - сопоставление с образцом. Уже отмечалось, что функции в Haskell'е задаются набором равенств. Иметь более одного равенства для функции есть смысл только тогда, когда правая часть этих равенств изменятся в зависимости от значений аргументов функции в левой части. Именно различные ограничения на вид аргументов и позволяют определить, какое из правил должно быть выбрано. Подобные ограничения на вид аргументов как раз и называются «образцами».

Использование образцов достаточно удобно и естественно. В большинстве случаев использование образцов позволяет получить значительно ясную и простую запись, чем анализ значения аргументов с помощью условных операторов.

Рассмотрим функции times и ехрn.

times x 0 = 0

times х у = х + times х (у - 1 )

expn 0 х у = times х у

eхрn n х 0 = 1

expn n х у = ехрn (n-1) х (ехрn n х (у-1))

В определении times имеется два образца:

«x 0», означающий, что первый аргумент - любое число, а второй – в точности 0;

«x y», означающий, что и первый, и второй аргумент - любые числа.

Вообще говоря, эти образцы, сами по себе, никакой информации о том, что x и y – это числа, не несут. Это следует только из типа функции.

В определении expn, аналогичным образом, три образца:

«0 x y», «n x 0», «n x y».

Можно заметить, что самый общий образец идет последним. Это не случайно и связано с процедурой обработки образцов, называемой сопоставлением с образцом. В процессе сопоставления устанавливается, соответствует ли каждый из аргументов форме своего образца. Если сопоставление произведено успешно, то правая часть вычисляется и возвращается как результат применения функции. Если нет, то пробуется следующий из образцов, содержащихся в определении функции. Если подходящий образец так и не найден, то возвращается ошибка.

Таким образом, например, для функции ехрn

образцу expn 0 1 2 будет сопоставлена правая часть первого правила

а, образцу expn 2 1 2 будет сопоставлена правая часть третьего правила.

Сопоставление с образцом можно применять и в операторе case:

Ещё одно определение функции длины списка:

my_length s = case s of

[] -> 0

(_:xs) -> 1 + my_length xs