Eu encontrei este código no manual An Introduction to Programming in Emacs Lisp
demonstrando recursão com a ajuda da cond
função para descobrir o número de seixos com base no número digitado de linhas, ou seja, se linhas = 2, então seixos devem ser 3, se 4 linhas, então devem ser 10 seixos há.
(defun triangle-using-cond (number)
(cond ((<= number 0) 0)
((= number 1) 1)
((> number 1)
(+ number (triangle-using-cond (1- number))))))
avalie para 10 após passar o argumento 4:
(triangle-using-cond 4)
O manual não explicou claramente o que acontece em cada etapa deste código de exemplo em particular e não consegui descobrir como a recursão funciona aqui. Você pode me ajudar a entender a mecânica passo a passo, o que acontece em cada instância?
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.1-4 = 3
,. Agora a chamada recursiva será(triangle-using-cond 3)
, mas isso terminará com a mesma chamada recursiva repetidamente até atingir o 1 condicional, certo? O que vai acontecer à seguir?(triangle-using-cond 3)
?1-
tem um nome particularmente enganador, especialmente se você ler uma chamada como se fosse uma notação infix. Retorna seu argumento menos um; NÃO menos um argumento.Respostas:
Usando "depuração printf"
Você pode deixar o Emacs ajudá-lo a entender, modificando a definição da função:
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 pressionandoC-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 pressioneC-M-x
para reavaliar a definição.Usando o depurador Emacs padrão
M-x debug-on-entry triangle-using-cond
, quandotriangle-using-cond
for chamado, você será colocado no depurador Emacs (buffer*Backtrace*
).Passe pela avaliação usando
d
(ouc
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 um1
ou um número menor ou igual a zero.O resultado do caso recursivo é, portanto:
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édio1
. Toda a expressão é(+ 4 (+ 3 (+ 2 1)))
. Avaliar que de dentro para fora e você tem:(+ 4 (+ 3 3))
,(+ 4 6)
, ou apenas10
.fonte
message (...)
, pressionandoC-x C-e
apenas mostra o resultado final (10) nada mais? Estou esquecendo de algo?Edebug
em ação?C-u C-M-x
com point dentro da função para edebug. Em seguida, basta executar a função normalmente.(message ...)
material imprime no*Message*
buffer.O Modelo de Substituição para Aplicação de Procedimentos do SICP pode explicar o algoritmo para entender códigos como este.
Eu escrevi algum código para facilitar isso também.
lispy-flatten
do pacote lispy faz isso. Aqui está o resultado da aplicaçãolispy-flatten
de(triangle-using-cond 4)
:Você pode simplificar a expressão acima para apenas:
Em seguida, aplainar mais uma vez:
O resultado final:
fonte
Isso não é específico para o Emacs / Elisp, mas se você tem formação em matemática, a recursão é como indução matemática . (Ou, se você não aprender: então, quando você aprende indução, é como recursão!)
Vamos começar com a definição:
Quando
number
é4
, nenhuma das duas primeiras condições é válida, portanto é avaliada de acordo com a terceira condição:(triangle-using-cond 4)
é avaliada como(+ number (triangle-using-cond (1- number)))
, a saber, como(+ 4 (triangle-using-cond 3))
.Da mesma forma,
(triangle-using-cond 3)
é avaliado como(+ 3 (triangle-using-cond 2))
.Da mesma forma,
(triangle-using-cond 2)
é avaliado como(+ 2 (triangle-using-cond 1))
.Mas para
(triangle-using-cond 1)
, a segunda condição é válida e é avaliada como1
.Um conselho para quem está aprendendo recursão: tente evitar
Se você está tentando se convencer de
(triangle-using-cond 4)
que retornará a resposta correta, basta supor que(triangle-using-cond 3)
ela retornará a resposta correta e verifique se ela estará correta nesse caso. Claro que você também deve verificar o caso base.fonte
As etapas de cálculo para o seu exemplo seriam as seguintes:
A condição 0 nunca é atendida porque 1 como entrada já encerra a recursão.
fonte
(1)
não é uma expressão válida.M-x calc
. :-) Falando sério, pretendia mostrar o cálculo, não a avaliação do Lisp.(4 +
em vez de(+ 4
em sua resposta ... :)Eu acho que é bem fácil, você não precisa do emacs lisp, é apenas matemática do ensino fundamental.
f (0) = 0
f (1) = 1
f (n) = f (n-1) + n quando n> 1
então f (5) = 5 + f (4) = 5 + 4 + f (3) = 5 + 4 + 3 + 2 + 1 + 0
Agora é óbvio.
fonte