Хотя отладка с помощью заявлений печати является обычной и даже иногда полезной, и для этой цели можно использовать io:format
, Erlang as already noted, Erlang предоставляет мощные встроенные функции трассировки, которые вы должны использовать вместо этого.
Предположим, что ваши функции highest_value/2
и divide/1
находятся в модуле с именем hv
.Во-первых, мы собираем hv
в оболочке Эрланга:
1> c(hv).
{ok,hv}
Далее мы используем Erlang's dbg
module для включения трассировки на hv
функции:
2> dbg:tracer().
{ok,<0.41.0>}
3> dbg:p(self(),call).
{ok,[{matched,[email protected],26}]}
4> dbg:tpl(hv,c).
{ok,[{matched,[email protected],5},{saved,c}]}
В команде 2 мы включить трассировку отладки и в команде 3 мы указываем что мы хотим отслеживать вызовы функций в нашем текущем процессе (возвращается self()
). В команде 4 мы создаем трассировку вызова с использованием встроенной спецификации трассировки c
по всем функциям модуля hv
.
После трассировка отладки включена, мы называем hv:divide/1
и начинается вывод трассировки:
5> hv:divide([4,8,12,16]).
(<0.34.0>) call hv:divide([4,8,12,16]) ({erl_eval,do_apply,6})
(<0.34.0>) call hv:'-divide/1-lc$^0/1-0-'([4,8,12,16],[4,8,12,16]) ({erl_eval,
do_apply,
6})
(<0.34.0>) call hv:highest_value([4,8,12,16],0) ({hv,'-divide/1-lc$^0/1-0-',2})
(<0.34.0>) call hv:highest_value([[8,12,16]],4) ({hv,'-divide/1-lc$^0/1-0-',2})
(<0.34.0>) call hv:highest_value([[]],[8,12,16]) ({hv,'-divide/1-lc$^0/1-0-',2})
(<0.34.0>) call hv:highest_value([[]],[8,12,16]) ({hv,'-divide/1-lc$^0/1-0-',2})
...
Во-первых, обратите внимание, что я сокращенный вывод трассировки, потому что в ...
момент это уже в бесконечном цикле, а остальные трассы идентичны двум утверждениям до ...
.
Что говорит нам трассировка? В первой строке отображается вызов функции divide/1
, а во второй строке показан вызов для понимания списка внутри divide/1
. Затем мы видим звонки на номер highest_value/2
, сначала с полным списком и N
, равным 0. Следующий вызов, где он становится интересным: потому что ваш код проходит [T]
, а не T
в качестве первого аргумента в рекурсивном вызове highest_value/2
, H
имеет значение [8,12,16]
, который лечит Эрланга как больше, чем текущее значение N
4, так что следующий рекурсивный вызов является:
highest_value([T], [8,12,16]).
и потому, что это T
[]
, это превращается в:
highest_value([[]], [8,12,16]).
Здесь H
- []
, а T
- также []
. H
не больше [8,12,16]
, поэтому все оставшиеся рекурсивные вызовы после этой точки идентичны этому, а рекурсия бесконечна.
Чтобы это исправить, вам необходимо пройти T
правильно as already noted:
highest_value([H|T], N) when H > N, H > 0 ->
highest_value(T, H);
highest_value([_|T], N) ->
highest_value(T, N).
Перекомпилируйте, который также перезагружает ваш модуль, и из-за того, что вам также необходимо настроить отладку трассировки снова:
5> c(hv).
{ok,hv}
6> dbg:tpl(hv,c).
{ok,[{matched,[email protected],5},{saved,c}]}
7> hv:divide([4,8,12,16]).
(<0.34.0>) call hv:divide([4,8,12,16]) ({erl_eval,do_apply,6})
(<0.34.0>) call hv:'-divide/1-lc$^0/1-0-'([4,8,12,16],[4,8,12,16]) ({erl_eval,
do_apply,
6})
(<0.34.0>) call hv:highest_value([4,8,12,16],0) ({hv,'-divide/1-lc$^0/1-0-',2})
(<0.34.0>) call hv:highest_value([8,12,16],4) ({hv,'-divide/1-lc$^0/1-0-',2})
(<0.34.0>) call hv:highest_value([12,16],8) ({hv,'-divide/1-lc$^0/1-0-',2})
(<0.34.0>) call hv:highest_value([16],12) ({hv,'-divide/1-lc$^0/1-0-',2})
(<0.34.0>) call hv:highest_value([],16) ({hv,'-divide/1-lc$^0/1-0-',2})
** exception error: no true branch found when evaluating an if expression
in function hv:highest_value/2 (/tmp/hv.erl, line 5)
in call from hv:'-divide/1-lc$^0/1-0-'/2 (/tmp/hv.erl, line 15)
Трассировка теперь показывает, что highest_value/2
работает, как ожидалось, но теперь мы попали новые проблемы с if
заявление, и исправить это уже объяснено in another answer, поэтому я не буду повторять его здесь.
Как вы можете видеть, трассировка Erlang намного мощнее, чем использование «отладки печати».
- Он может быть включен и выключен интерактивно в оболочке Erlang по мере необходимости.
- В отличие от отладки других языков, трассировка отладки не требует специальных флагов компиляции для ваших модулей.
- В отличие от отладочных операторов печати, вам не нужно менять код и повторно перекомпилировать.
То, что я показал здесь, едва царапает поверхность до тех пор, пока возможности трассировки Erlang не исчезнут, но этого было более чем достаточно, чтобы найти и исправить проблемы.
И, наконец, обратите внимание, что при использовании lists:max/1
стандартной библиотеки вызова вы можете легко добиться того, что ваш модуль пытается сделать:
divide(L) ->
case lists:max(L) of
N when N > 0 ->
[V/N || V <- L];
_ ->
error(badarg, [L])
end.
Обратите внимание, что ваш '/ функция деления 1' нерационально пересчитывает наибольшее значение в список для каждого элемента, обработанного в понимании списка. Вы должны сначала называть 'high_value (_L, 0)', привязать его возвращаемое значение к переменной и использовать переменную в качестве делителя в понимании списка. –