52700.fb2 Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 201

Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 201

выразить энергичный образец через функцию seq.

Подсказка: За счёт семантики case-выражений нам не нужно специальных конструкций для того чтобы

реализовать seq в STG:

seq = FUN( a b ->

case a of

x -> b

)

При этом вызов функции seq будет встроен. Необходимо будет заменить в коде все вызовы seq на пра-

вую часть определения (без FUN). Также обратите внимание на то, что плюс не является примитивной

функцией:

plusInt = FUN( ma mb ->

case ma of

I# a -> case mb of

I# b -> case (primitivePlus a b) of

res -> I# res

)

В этой функции всплыла на поверхность одна тонкость. Если бы мы писали это выражение в Haskell,

то мы бы сразу вернули результат (I#(primitivePlus a b)), но мы пишем в STG и конструктор может

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

let-выражении:

-> let v = primitivePlus a b

in

I# v

Но это не правильное выражение в STG! Конструкция в правой части let-выражения должна быть объ-

ектом кучи, а у нас там простое выражение. Но было бы плохо добавить к нему THUNK, поскольку это

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

няется очень быстро. Было бы плохо создавать для неё специальный объект на куче. Поэтому мы сразу

вычисляем это выражение в третьем case. Эта функция также будет встроенной, необходимо заменить

все вызовы на определение.

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

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

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

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

искали решение. Я говорил, что такие алгоритмы очень эффективны при ленивой стратегии вычис-

лений, но так ли это? Будьте критичны, не верьте на слово, ведь теперь у вас есть инструменты для

проверки моих туманных гипотез.

• Откройте документацию к GHC. Пролистайте её. Проникнитесь уважением к разработчикам GHC. Най-

дите исходники GHC и почитайте их. Посмотрите на Haskell-код, написанный профессионалами. Вы-

берите функцию наугад и попытайтесь понять как она строит свой результат.

Упражнения | 179

• Откройте документацию вновь. Нас интересует глава Profiling. Найдите в разделе профилирование

кучи как выполняется retainer profiling. Это специальный тип профилирования направленный на по-

иск данных, которые удерживают в памяти другие данные (типичный сценарий для утечек памяти).

Разберитесь с этим типом профилирования (флаг hr).

• Постройте систему правил, которая выполняет слияние для списков List, определённых в примере для

прагмы RULES. Сравните показатели производительности с правилами и без (для этого скомпилируйте

дважды с флагом O и без) на тестовом выражении:

main = print $ sumL $

mapL (\x -> x - 1000) $ mapL (+100) $ mapL (*2) $ genL 0 1e6

Функция sumL находит сумму элементов в списке, функция genL генерирует список чисел с единичным