Usando "depuração printf"
Você pode deixar o Emacs ajudá-lo a entender, modificando a definição da função:
(defun triangle-using-cond (number)
(message (format "called with %d" number))
(cond ((<= number 0) 0)
((= number 1) 1)
((> number 1)
(+ number (triangle-using-cond (1- number))))))
Basta adicionar um (message ...)
lugar para imprimir uma trilha no *Messages*
buffer.
Usando Edebug
Coloque o ponto em qualquer lugar dentro da definição da função e pressione C-u C-M-x
"instrumentar". Em seguida, avalie a função, por exemplo, colocando o ponto depois (triangle-using-cond 3)
e pressionando C-x C-e
.
Agora você está no modo Edebug. Pressione a barra de espaço para percorrer a função. Os valores intermediários de cada expressão são mostrados na área de eco. Para sair do modo Edebug, basta pressionar q
. Para remover a instrumentação, coloque um ponto em qualquer lugar dentro da definição e pressione C-M-x
para reavaliar a definição.
Usando o depurador Emacs padrão
M-x debug-on-entry triangle-using-cond
, quando triangle-using-cond
for chamado, você será colocado no depurador Emacs (buffer *Backtrace*
).
Passe pela avaliação usando d
(ou c
para pular as avaliações desinteressantes).
Para ver o estado intermediário (valores variáveis, etc.), você pode usar a e
qualquer momento. Você é solicitado a inserir um sexp para avaliar e o resultado da avaliação é impresso.
Enquanto você usa o depurador, mantenha uma cópia do código-fonte visível em outro quadro, para poder acompanhar o que está acontecendo.
Você também pode inserir chamadas explícitas para inserir o depurador (mais ou menos pontos de interrupção) em locais arbitrários no código-fonte. Você insere (debug)
ou (debug nil SOME-SEXP-TO-EVALUATE)
. No último caso, quando o depurador é inserido, SOME-SEXP-TO-EVALUATE
é avaliado e o resultado é impresso. (Lembre-se de que você pode inserir esse código no código-fonte e usá C-M-x
-lo para avaliá-lo e desfazer - você não precisa salvar o arquivo editado.)
Consulte o manual do Elisp, nó Using Debugger
para obter mais informações.
Recursão como um loop
Enfim, pense em recursão como um loop. Existem dois casos de rescisão definidos: (<= number 0)
e (= number 1)
. Nesses casos, a função retorna um número simples.
No caso recursivo, a função retorna a soma desse número e o resultado da função com number - 1
. Eventualmente, a função será chamada com um 1
ou um número menor ou igual a zero.
O resultado do caso recursivo é, portanto:
(+ number (+ (1- number) (+ (1- (1- number)) ... 1)
Tome por exemplo (triangle-using-cond 4)
. Vamos acumular a expressão final:
na primeira iteração number
é 4
, então a (> number 1)
ramificação é seguida. Começamos a construir uma expressão (+ 4 ...
e chamamos a função com (1- 4)
, ie (triangle-using-cond 3)
.
agora number
é 3
e o resultado é (+ 3 (triangle-using-cond 2))
. A expressão total do resultado é (+ 4 (+ 3 (triangle-using-cond 2)))
.
number
é 2
agora, então a expressão é(+ 4 (+ 3 (+ 2 (triangle-using-cond 1))))
number
é 1
agora, e tomamos o (= number 1)
ramo, resultando em um tédio 1
. Toda a expressão é (+ 4 (+ 3 (+ 2 1)))
. Avaliar que de dentro para fora e você tem: (+ 4 (+ 3 3))
, (+ 4 6)
, ou apenas 10
.
triangle-using-cond
com o argumento 1 menor que qualquer que seja o número. As condições vão na ordem de a, bec, e c - o que corresponder primeiro, é onde o dinheiro para.