Читаем Учебник по Haskell полностью

При этом нам приходится давать какое-нибудь имя предикату, например p. С помощью безымянной функ-

ции мы могли бы написать так:

f :: [Int] -> [Int]

f = filter (\x -> x > 2 && x < 10 && even x)

Смотрите мы составили предикат сразу в аргументе функции filter. Выражение (\x -> x > 2 && x <

10 && even x) является обычным значением.

Возможно у вас появился вопрос, где аргумент функции? Где тот список по которому мы проводим филь-

трацию. Ответ на этот вопрос кроется в частичном применении. Давайте вычислим по правилу применения

тип функции filter:

f :: (a -> Bool) -> [a] -> [a],

x :: (Int -> Bool)

------------------------------------------------------

(f x) :: [Int] -> [Int]

После применения параметр a связывается с типом Int, поскольку при применении происходит сопостав-

ление более общего предиката a -> Bool из функции filter с тем, который мы передали первым аргументом

Int -> Bool. После этого мы получаем тип (f x) :: [Int] -> [Int] это как раз тип функции, которая прини-

мает список целых чисел и возвращает список целых чисел. Частичное применение позволяет нам не писать

в таких выражениях:

f xs = filter p xs

where p x = ...

последний аргумент xs.

К примеру вместо

Определение функций | 65

add a b = (+) a b

мы можем просто написать:

add = (+)

Такой стиль определения функций называют бесточечным (point-free).

Давайте выразим функцию filter с помощью лямбда-функций:

filter :: (a -> Bool) -> ([a] -> [a])

filter = \p -> \xs -> case xs of

[]

-> []

(x:xs) -> let rest = filter p xs

in

if

p x

then x : rest

else rest

Мы определили функцию filter пользуясь только элементами композиционного стиля. Обратите внима-

ние на скобки в объявлении типа функции. Я хотел напомнить вам о том, что все функции в Haskell являются

функциями одного аргумента. Это определение функции filter как нельзя лучше подчёркивает этот факт.

Мы говорим, что функция filter является функцией одного аргумента p в выражении \p -> , которая возвра-

щает также функцию одного аргумента. Мы выписываем это в явном виде в выражении \xs -> . Далее идёт

выражение, которое содержит определение функции.

Отметим, что лямбда функции могут принимать несколько аргументов, в предыдущем определении мы

могли бы написать:

filter :: (a -> Bool) -> ([a] -> [a])

filter = \p xs -> case xs of

...

но это лишь синтаксический сахар, который разворачивается в предыдущую запись.

Для тренировки определим несколько стандартных функций для работы с кортежами с помощью лямбда-

функций (все они определены в Prelude):

fst :: (a, b) -> a

fst = \(a, _) -> a

snd :: (a, b) -> b

snd = \(_, b) -> b

swap :: (a, b) -> (b, a)

swap = \(a, b) -> (b, a)

Обратите внимание на то, что все функции словно являются константами. Они не содержат аргументов.

Аргументы мы “пристраиваем” с помощью безымянных функций.

Определим функции преобразования первого и второго элемента кортежа (эти функции определены в

модуле Control.Arrow)

first :: (a -> a’) -> (a, b) -> (a’, b)

first = \f (a, b) -> (f a, b)

second :: (b -> b’) -> (a, b) -> (a, b’)

second = \f (a, b) -> (a, f b)

Также в Prelude есть полезные функции, которые превращают функции с частичным применением в

обычны функции и наоборот:

curry :: ((a, b) -> c) -> a -> b -> c

curry = \f -> \a -> \b -> f (a, b)

uncurry :: (a -> b -> c) -> ((a, b) -> c)

uncurry = \f -> \(a, b) -> f a b

66 | Глава 4: Декларативный и композиционный стиль

Функция curry принимает функцию двух аргументов для которой частичное применение невозможно.

Это имитируется с помощью кортежей. Функция принимает кортеж из двух элементов. Функция curry (от

слова каррирование, частичное применение) превращает такую функцию в обычную функцию Haskell. А

функция uncurry выполняет обратное преобразование.

С помощью лямбда-функций можно имитировать локальные переменные. Так например можно перепи-

сать формулу для вычисления площади треугольника:

square a b c =

(\p -> sqrt (p * (p - a) * (p - b) * (p - c)))

((a + b + c) / 2)

Смотрите мы определили функцию, которая принимает параметром полупериметр p и передали в неё

значение ((a + b + c) / 2). Если в нашей функции несколько локальных переменных, то мы можем

составить лямбда-функцию от нескольких переменных и подставить в неё нужные значения.

4.5 Какой стиль лучше?

Основной критерий выбора заключается в том, сделает ли этот элемент код более ясным. Наглядность

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

Далее мы рассмотрим несколько примеров определений из Prelude и подумаем, почему был выбран тот

или иной стиль. Начнём с класса Ord и посмотрим на определения по умолчанию:

-- Тип упорядочивания

data

Ordering

=

LT | EQ | GT

deriving (Eq, Ord, Enum, Read, Show, Bounded)

class

(Eq a) => Ord a

where

compare

:: a -> a -> Ordering

(< ), (<=), (>=), (> ) :: a -> a -> Bool

max, min

:: a -> a -> a

-- Минимальное полное определение:

--

(<=) или compare

Перейти на страницу:

Похожие книги

1С: Бухгалтерия 8 с нуля
1С: Бухгалтерия 8 с нуля

Книга содержит полное описание приемов и методов работы с программой 1С:Бухгалтерия 8. Рассматривается автоматизация всех основных участков бухгалтерии: учет наличных и безналичных денежных средств, основных средств и НМА, прихода и расхода товарно-материальных ценностей, зарплаты, производства. Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, проводить их по учету, формировать разнообразные отчеты, выводить данные на печать, настраивать программу и использовать ее сервисные функции. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов.Для широкого круга пользователей.

Алексей Анатольевич Гладкий

Программирование, программы, базы данных / Программное обеспечение / Бухучет и аудит / Финансы и бизнес / Книги по IT / Словари и Энциклопедии
1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

Финансы / Программирование, программы, базы данных