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

сания, затем он скажет разверну сначала sum’, эта функция запросит первый элемент списка, что приведёт

к вызову filter. Фильтр будет запрашивать следующий элемент списка у подчинённых ему функций до

тех пор, пока предикат p не вернёт True на одном из них. Всё это время функция map будет вытягивать из

produce по одному элементу. Причём память, выделенная на промежуточные не нужные значения (на них

p вернул False) будет переиспользована. Как только sum’ прибавит первый элемент, она запросит следую-

щий, проснётся фильтр и так далее. Вся функция будет работать в постоянном ограниченном объёме памяти,

который не зависит от величины списка longList!

Примерам ленивых вычислений будет посвящена отдельная глава, а пока приведём один пример. Найдём

корень уравнения с помощью метода неподвижной точки. У нас есть функция f :: a -> a, и нам нужно

найти решение уравнения:

f x = x

Можно начать с какого-нибудь стартового значения, и подставлять, подставлять, подставлять его в f до

тех пор, пока значение не перестанет изменяться. Так мы найдём решение.

x1 = f x0

x2 = f x1

x3 = f x2

...

до тех пор пока abs (x[N] - x[N-1]) <= eps

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

рации: минус, поиск абсолютного значения и сравнение на больще/меньше. Тип нашей функции:

f :: (Ord a, Num a) => a -> a

Ленивые вычисления позволяют нам отделить шаг генерации решений, от шага проверки сходимости.

Сначала мы сделаем список всех подстановок функции f, а затем найдём в этом списке два соседних элемента

расстояние между которыми достаточно мало. Итак первый шаг, генерируем всю последовательность:

Пример ленивых вычислений | 151

xNs = iterate f x0

Мы воспользовались стандартной функцией iterate из Prelude. Теперь ищем два соседних числа:

converge :: (Ord a, Num a) => a -> [a] -> a

converge eps (a:b:xs)

| abs (a - b) <= eps

= a

| otherwise

= converge eps (b:xs)

Поскольку список бесконечный мы можем не проверять случаи для пустого списка. Итоговое решение:

roots :: (Ord a, Num a) => a -> a -> (a -> a) -> a

roots eps x0 f = converge eps $ iterate f x0

За счёт ленивых вычислений функции converge и iterate работают синхронно. Функция converge запра-

шивает новое значение и iterate передаёт его, но только одно! Найдём решение какого-нибудь уравнения.

Запустим интерпретатор. Мы ленимся и не создаём новый модуль для такой “большой” функции. Опреде-

ляем её сразу в интерпретаторе.

Prelude> let converge eps (a:b:xs) = if abs (a-b)<=eps then a else converge eps (b:xs) Prelude> let roots eps x0 f = converge eps $ iterate f x0

Найдём корень уравнения:

x( x − 2) = 0

x 2 2 x = 0

1 x 2 = x

2

Prelude> roots 0.001 5 (\x -> x*x/2)

Метод завис, остаётся только нажать ctrl+c для остановки. На самом деле есть одно условие для сходи-

мости метода. Метод сойдётся, если модуль производной функции f меньше единицы. Иначе есть возмож-

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

d 1 x 2 = x

dx 2

Нам следует ожидать решения в интервале от минус единицы до единицы:

Prelude> roots 0.001 0.5 (\x -> x*x/2)

3.0517578125e-5

Мы нашли решение, корень равен нулю. В этой записи Ne-5 означает N · 10 5

9.5 Краткое содержание

В этой главе мы узнали о том как происходят вычисления в Haskell. Мы узнали, что они ленивые. Всё

вычисляется как можно позже и как можно меньше. Такие вычисления называются вычислениями по необ-

ходимости.

Также мы узнали о вычислениях по значению и вычислениях по имени.

• В вычислениях по значению редукция проводится от листьев дерева выражения к корню

• В вычислениях по имени редукция проводится от корня дерева выражения к листьям.

152 | Глава 9: Редукция выражений

Вычисление по необходимости является улучшением вычисления по имени. Мы не дублируем выражения

во время применения. Мы сохраняем значения в памяти и подставляем в функцию ссылки на значения. После

вычисления значения происходит его обновление в памяти. Так если в одном месте выражение уже было

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

считаем готовое значение.

Мы познакомились с терминологией процесса вычислений. Выражение может находится в нормальной

форме. Это значит что оно вычислено. Может находится в слабой заголовочной нормальной форме. Это значит,

что мы знаем хотя бы один конструктор в корне выражения. Также возможно выражение ещё не вычислялось,

тогда оно является отложенным (thunk).

Суть ленивых вычислений заключается в том, что они происходят синхронно. Если у нас есть композиция

двух функций:

g ( f x)

Внутренняя функция f не начнёт вычисления до тех пор пока значения не понадобятся внешней функции

g. О последствиях этого мы остановимся подробнее в отдельной главе. Значения могут потребоваться только

при сопоставлении с образцом. Когда мы хотим узнать какое из уравнений нам выбрать.

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

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

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

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

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

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

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

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

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