Главная » Haskell » Анонимные функции

0

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

Эта возможность основана на методах ?-исчисления, которое является ядром языка Haskell. Через ?-нотацию можно определять любую функцию, в том числе и анонимную, поскольку любая  ?-абстракция является безымянной функцией. Необходимо отметить, что здесь не будет описываться теория ?-исчисления, поскольку это выходит далеко за рамки справочника. В любом случае тот читатель,

который не знаком с этим формализмом, может найти его описание в специализированной литературе.

Использовать ?-исчисление в языке Haskell несложно. Ниже показан пример, где определены функции multiply и double именно при помощи ?-исчисления.

multiply = \x y  ->  x  * y

double  = \x ->  x  * 2

Эти определения функций записаны в полном соответствии с такими же определениями, которые могут быть сделаны при помощи ?-выражений:

?xy.(x ? y),

?x.(x ? 2).

То есть видно, что ?-абстракции кодируются на языке Haskell просто. Символ (?)  заменяется  на символ (\),  а символ (.)  заменяется  на стрелку (->). Остальное  даётся без изменения (естественно, принимая во внимание синтаксис языка Haskell).

squares  = map  (\x ->  x^2)  [0..]

Этот пример показывает вызов анонимной функции, возводящей в квадрат переданный параметр (пример большей частью надуман, так как для этих целей нет особой надобности использовать анонимную функцию). Результатом выполнения этой инструкции будет  бесконечный  список квадратов целых чисел, начиная с нуля.

Необходимо отметить, что в языке Haskell используется упрощённый способ записи ?-выражений, так как в точной нотации функцию multiply из примера выше правильнее было бы написать как

multiply = \x ->  \y ->  x  * y

что в большей мере соответствует  точной математической записи:

?x.?y.(x ? y).

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

1.4.6.    Охрана

Охрана, или охраняющее выражение, — это логическое выражение (то есть возвращающее значение типа Bool), которое накладывает некоторые ограничения на используемые образцы. Охрана может применяться в языке Haskell в качестве дополнительной возможности к технологии сопоставления с образцом. При определении функций охраняющие выражения  записываются после образцов, но перед выражениями, являющими собой описание вычислительного  процесса. Для разграничения охраняющих выражений и образцов используется символ (|). Например, следующим образом можно определить функцию для получения знака числа:

sign  ::  Integer ->  Int sign  x  |  x  < 0   =  -1

| x  == 0 = 0

| x  > 0   = 1

Как  и в случае с процессом сопоставления  с образцами,  просмотр охраняющих выражений производится сверху вниз до  первого выражения, значение которого равно True. В этом случае значением функции будет то, что записано после найденной охраны. После этого вычислительный  процесс останавливается, поиск среди оставшихся охраняющих выражений не производится. Именно поэтому обычно в самом конце списка охраны и записывается слово otherwise — оно «поймает» вычисления в любом случае, даже если никакое иное охраняющее выражение не было истинно. Это слово не является ключевым, оно определено в качестве константной функции в стандартном модуле Prelude и является синонимом значения True. Сделано это для удобства записи и чтения исходных кодов программ (слово «otherwise» по-английски обозначает «в противном случае»).

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

sign’ x  | x  < 0 = -1

| x  > 0 = 1 sign’ _                 =  0

В книге [1] была допущена ошибка относительно этой записи. Было  сказано, что такая запись содержит одну логическую ошибку — если подать на вход функции sign’ число 0, то произойдет ошибка  времени  выполнения, так  как транслятор не сможет найти подходящее выражение для вычисления. Это связано с тем, что при сопоставлении с образцами число 0 на входе подойдёт к первому клозу, после чего ни одно из охраняющих выражений не позволит вычислить значение функции. А раз сопоставление с образцами произошло успешно, клоз был выбран, то сам процесс сопоставления уже остановлен.

Однако это не так. Современные трансляторы языка Haskell распознают такие определения, а потому если подать на вход функции sign’ значение 0, результат  будет успешно вычислен — процесс сопоставления с образцами войдёт во второй клоз и позволит определить результат выполнения функции. Тем не менее крайне не рекомендуется использовать подобные определения.

Охраняющие выражения могут также использоваться и в сочетании с ключевым словом case, когда после выбора альтернативы  по условию дополнительно производится выбор по охраняющему выражению. Например:

sign’’ x  = case x  of

0                 ->  0

_ | x  <  0 ->  -1

| x  >  0 ->  1

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

Остаётся отметить, что механизм охраны в языке Haskell используется тогда, когда сложно использовать технологию сопоставления с образцами. Так, к примеру, это сложно сделать в прикладных функциях, которые обслуживают классы определённых  типов значений, в которых доподлинно неизвестен тип обрабатываемых значений, поэтому использование конструкторов  данных в  образцах попросту невозможно (подробно см. главу 3.).

Источник: Душкин Р. В., Справочник по языку Haskell. М.: ДМК Пресс, 2008. 544 с., ил.

По теме:

  • Комментарии