Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Функциональное программирование. Конспект лекций

.pdf
Скачиваний:
0
Добавлен:
15.08.2026
Размер:
1 Мб
Скачать

для просмотра значения переменной var.

Для продолжения выполнения программы вводится команда

:continue.

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

9.3. Трассировка

При отладке программы часто интересно знать последовательность выполнения команд и вызовов процедур и функций. Императивные отладчики обычно предоставляют функцию отслеживания стека, которая позволяет видеть стек активных вызовов функций, описывающих путь к текущему местоположению. К сожалению, это трудно обеспечить в Haskell, потому что выполнение происходит на основе требований и необходимости вычисления выражений. Тем не менее в Haskell предоставляется способ возврата от точки прерывания к предыдущим этапам оценки, т.е. это переход на один шаг назад для получения информации о ходе вызовов функций.

Для трассировки и дальнейшего анализа используются коман-

ды:

:trace

:history

:back :forward.

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

*Main> :list qsort

1qsort [] = []

2qsort (a:as) = qsort left ++ [a] ++ qsort right

3where (left,right) = (filter (<=a) as, filter (>a) as)

*Main> :b 1

Breakpoint 1 activated at qsort.hs:1:11-12 *Main>

101 –

Точка прерывания установлена в строке 1, т.е. qsort [] = []. Запуск трассировки:

*Main> :trace qsort [3,2,1] Stopped at qsort.hs:1:11-12 _result :: [a] [qsort.hs:1:11-12] *Main>

Просмотр результатов трассировки:

[qsort.hs:1:11-12] *Main> :history -1 : qsort.hs:3:24-38

-2 : qsort.hs:3:23-55

-3 : qsort.hs:(1,0)-(3,55) -4 : qsort.hs:2:15-24

-5 : qsort.hs:2:15-46 -6 : qsort.hs:3:24-38 -7 : qsort.hs:3:23-55

-8 : qsort.hs:(1,0)-(3,55) -9 : qsort.hs:2:15-24

-10 : qsort.hs:2:15-46 -11 : qsort.hs:3:24-38 -12 : qsort.hs:3:23-55

-13 : qsort.hs:(1,0)-(3,55) -14 : qsort.hs:2:15-24

-15 : qsort.hs:2:15-46

-16 : qsort.hs:(1,0)-(3,55) <end of history>

Просмотр переменных и выражений на каждом шаге выполня-

ется с помощью команд :back, :forward:, :print var, :force var.

[qsort.hs:1:11-12] *Main> :back Logged breakpoint at qsort.hs:3:24-38 _result :: [a]

as :: [a]

– 102 –

a :: a

[-1: qsort.hs:3:24-38] *Main> *Main> :print a

a = 2

Команда :trace может использоваться без указания выражения. В этом случае трассировка начинается с текущей точки останова. Команда :history доступна только при использовании :trace. По умолчанию отладчик запоминает последние 50 шагов в истории, но это можно изменить с помощью команды:

:set -fghci-hist-size = n.

9.4. Исключения

Исключения могут возникнуть при ошибках, и при этом отсутствует контекстная информация. Поиск того, какой именно вызов в программе привел к ошибке, может быть кропотливым процессом, обычно с использованием Debug.Trace.trace или компиляцией с профилированием и использованием Debug.Trace.traceStack.

Отладчик Haskell предлагает способ пролить свет на эти ошибки быстро, без изменения или перекомпиляции исходного кода. Одним из способов было бы установить точку прерывания на месте в исходном коде, которое генерирует исключение, а затем использовать :trace и :history для установления контекста. Однако часто функция, вызвавшая исключение, находится в библиотеке, и на нее нельзя установить точку прерывания напрямую. По этой причине Haskell предоставляет флаги -fbreak-on-exception, которые вызывают остановку оценщика при возникновении исключения, и -fbreak- on-error, который работает аналогичным образом, но останавливается только на неперехваченных исключениях. При остановке на исключении отладчик будет действовать так же, как и при достижении точки прерывания, с тем отличием, что не будет показан исходный код, например:

*Main> :set -fbreak-on-exception

*Main> :trace qsort ("abc" ++ undefined) “Stopped at <exception thrown>

– 103 –

_exception :: e

[<exception thrown>] *Main> :history -1 : qsort.hs:3:24-38

-2 : qsort.hs:3:23-55

-3 : qsort.hs:(1,0)-(3,55) -4 : qsort.hs:2:15-24

-5 : qsort.hs:2:15-46

-6 : qsort.hs:(1,0)-(3,55) <end of history>

[<exception thrown>] *Main> :back Logged breakpoint at qsort.hs:3:24-38 _result :: [a]

as :: [a] a :: a

[-1: qsort.hs:3:24-38] *Main> :force as

*** Exception: Prelude.undefined

[-1: qsort.hs:3:24-38] *Main> :print as as = 'b' : 'c' : (_t1::[Char])

В приведенном примере после возникновения исключения выдается команда :history, и с помощью команд :back и :force проводится анализ переменных.

9.5. Пример отладки

Рассмотрим пример отладки функции быстрой сортировки списка. Алгоритм быстрой сортировки заключается в том, что выбирается опорный элемент и список делится на два. В левом списке элементы меньше или равны опорному, а в правом – больше опорного. Далее процесс рекурсивно повторяется для левого и правого списков. Ниже представлена функция быстрой сортировки списков:

module TestTrace where

qsort [] = []

qsort (a:as) = qsort left ++ [a] ++ qsort right

– 104 –

where (left,right) = (filter (<=a) as, filter (>a) as)

9.5.1. Пошаговая отладка

Процесс пошаговой отладки можно представить в виде следующих шагов.

1.Установить параметры отладки:

:set stop :list

:set -fprint-evld-with-show.

2.Выдать команду :list qsort и посмотреть номера строк функ-

ции qsort:

*TestTrace> :list qsort 2

3qsort [] = []

4qsort (a:as) = qsort left ++ [a] ++ qsort right

5where (left,right) = (filter (<=a) as, filter (>a) as)

3. Задать точки останова и проверить корректность их установ-

ки:

TestTrace> :break 4

Breakpoint 0 activated at testTrace.hs:4:16-47 *TestTrace> :show breaks

[0] TestTrace testTrace.hs:4:16-47

4. Запустить программу на выполнение, введя имя функции и ее аргументы, например qsort [5,8,1,4,2]. Выполнение остановится в точке прерывания и будет выдана информация о точке останова, значениях связанных переменных в виде

[testTrace.hs:4:16-47] *TestTrace> :step Stopped at testTrace.hs:4:16-25

– 105 –

_result :: [t] = _ left :: [t] = _

3qsort [] = []

4qsort (a:as) = qsort left ++ [a] ++ qsort right

^^^^^^^^^^

5 where (left,right) = (filter (<=a) as, filter (>a) as)

Здесь символами «^» указывается место прерывания функции. 5. Ввести команды :print <имя переменной> или :force <имя переменной> для того, чтобы проанализировать значения перемен-

ных.

[testTrace.hs:5:42-55] *TestTrace> :print as as = [4,2]

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

[testTrace.hs:(3,1)-(5,56)] *TestTrace> :step Stopped at testTrace.hs:5:42-55

_result :: [Integer] = _ a :: Integer = 1

as :: [Integer] = [4,2]

4qsort (a:as) = qsort left ++ [a] ++ qsort right

5where (left,right) = (filter (<=a) as, filter

(>a) as)

^^^^^^^^^^^^^^

9.5.2. Трассировка

Для выполнения трассировки целесообразно добавить в исходную программу функцию trace или traceShow:

– 106 –

module TestTrace where

import Debug.Trace

qsort [] = []

qsort (a:as) = trace ("left:" ++ show left) (qsort left) ++ [a] ++ trace ("right:" ++ show right) (qsort right)

where (left,right) = (filter (<=a) as, filter (>a) as)

Функция trace находится в модуле Debug.Trace имеет тип trace :: String -> a -> a.

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

бавляться строка "left:" ++ show left или "right:" ++ show right, т.е.

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

При трассировке надо выполнить шаги с 1 по 3, которые описаны выше для пошаговой отладки. Далее выполняем следующие шаги.

4. Запустить трассировку командой :trace qsort [5,8,1,4,2]:

*TestTrace> :trace qsort [5,8,1,4,2] left:[1,4,2]

left:[]

Stopped at testTrace.hs:5:12-13 _result :: [a] = _

4

5 qsort [] = []

^^

6 qsort (a:as) = trace ("left:" ++ show left) (qsort left) ++ [a] ++ trace ("right:" ++ show right) (qsort right)

– 107 –

Трассировка продолжается до точки прерывания, установленной в строке 5. Точка прерывания выделена символами «^». Функция trace вывела информацию

left:[1,4,2],

что говорит о том, что в левый список попали элементы меньшие 5. 5. Выполнить команду :history и посмотреть «маршрут» вызова

выражений до точки прерывания:

[testTrace.hs:5:12-13] *TestTrace> :history -1 : qsort (testTrace.hs:(5,1)-(7,56))

-2 : qsort (testTrace.hs:6:46-55)

-3 : qsort:(...) (testTrace.hs:7:25-39) -4 : qsort:(...) (testTrace.hs:7:24-56) -5 : qsort (testTrace.hs:6:34-42)

-6 : qsort (testTrace.hs:6:23-42) -7 : qsort (testTrace.hs:6:16-56) -8 : qsort (testTrace.hs:6:16-111)

-9 : qsort (testTrace.hs:(5,1)-(7,56)) -10 : qsort (testTrace.hs:6:46-55)

-11 : qsort:(...) (testTrace.hs:7:25-39) -12 : qsort:(...) (testTrace.hs:7:24-56) -13 : qsort (testTrace.hs:6:34-42)

-14 : qsort (testTrace.hs:6:23-42) -15 : qsort (testTrace.hs:6:16-56) -16 : qsort (testTrace.hs:6:16-111)

-17 : qsort (testTrace.hs:(5,1)-(7,56)) <end of history>

Из представленной информации видно, что до первого прерывания выполнено 17 шагов (начало вычислений – шаг № -17, далее -16 и последний шаг -1). Для перемещения по истории вычислений можно использовать команды :back и :forward и проводить анализ переменных с помощью :print и :force.

6. Выполнить команду :continue и продолжить анализ:

[testTrace.hs:5:12-13] *TestTrace> :continue right:[8]

– 108 –

left:[]

,5Stopped at testTrace.hs:5:12-13 _result :: [a] = _

4

5 qsort [] = []

^^

6 qsort (a:as) = trace ("left:" ++ show left) (qsort left) ++ [a] ++ trace ("right:" ++ show right) (qsort right)

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

– 109 –

Список литературы

[1]Филд А., Харрисон П. Функциональное программирова-

ние. М.: Мир, 1993.

[2]Холомьев А. Учебник по Haskell // Книга зарегистрирована под лицензией Creative Commons Attribution-NonCommercial- NoDerivs 3.0 Generic license (CC BY-NC-ND 3.0), 2012.

[3]Роганова Н.А. Функциональное программирование: Учебное пособие. М.: ГИНФО, 2002.

[4]Официальный сайт языка Haskell. [Электронный ресурс.] –

Режим доступа: https://www.haskell.org (дата обращения: 15.04.2020).

[5]Душкин Р.В. 14 занимательных эссе о языке Haskell и функциональном программировании. М.: ДМК Пресс, 2011.

[6]Душкин Р.В. Справочник по языку Haskell. М.: ДМК Пресс,

2008.

– 110 –

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]