On Apr 30, 2:43 am, tarcisio praciano-pereira
<[email protected]> wrote:
> Eras, Pedro! explica o código - put some comments!  Não entendi nada, e
> sou matemático.
>
> Aparentemente fazes alguma seleção entre par e impar, mas não entendi
> porque.
> > (defun ! (n)
> >   (let ((shift 0))
> >     (labels ((fact1 (n m)
> >                (cond
> >                  ((and (evenp n) (> m 1))
> >                   (incf shift (ash n -1))
> >                   (fact1 (ash n -1) (ash m -1)))
> >                  ((<= n m) n)
> >                  (t (* (fact1 n (ash m 1)) (fact1 (- n m)(ash m 1)))))))
> >       (ash (fact1 n 1) shift))))
>
> > pedro

Ele usou otimização com bitshifting, uma operação a nível de máquina.
Não é pra ser compreendido para não iniciados. ;)

Fatorial é aquela definição recursiva e simples (e não otimizada) de
sempre:
(defun fatorial (n)
  (if (< n 2) 1
     (* n (fatorial (- n 1)))))

Exceto que uma implementação puramente recursiva desse tipo
rapidamente chega a um limite de execução:  a pilha estoura por haver
muitas chamadas para ns muito grandes.  A solução simples em
programação funcional é usar o estilo de tail calls (que a maioria das
implementações suporta).

Nesse estilo, elimina-se a necessidade de se manter várias chamadas em
aberto na pilha ao se eliminar a espera pelo termino de uma
computação:  a última chamada (tail call) é realmente uma chamada, não
uma expressão esperando pelo resultado de uma chamada.  Como nada
exceto a chamada original da função espera pelo seu resultado final,
por baixo dos panos pode-se simplesmente, a grosso modo, eliminar todo
o artefato de chamadas reais de função e compilá-la como se fosse uma
simples iteração ou um goto nomeado.  Não se gasta pilha de chamadas.

Ilustrando:

  (if (< n 2) 1 ; <- expressão final
     (* n (fatorial (- n 1))))) ; <- expressão final esperando pelo
resultado da chamada recursiva

a troca é simples e o benefício é imenso:
  (if (< n 2) 1
     (fatorial (- n 1)  (* n)))) ; trazemos a expressão final como um
parâmetro para a própria chamada

mas está incompleto, pois o resultado de (fatorial (- n 1)) ainda não
foi calculado e não podemos portanto multiplicá-lo a n, certo?  Bem,
digamos que possamos e chamemos a ele de resultado:

  (if (< n 2) resultado ; <- aqui
     (fatorial (- n 1)  (* n resultado)))) ; <- e aqui

agora vamos dar um valor inicial a resultado, que por acaso vai ser o
mesmo para a condição de parada original: 1

(defun fatorial (n &optional (resultado 1))
  (if (< n 2) resultado
     (fatorial (- n 1)  (* n resultado))))

Bem, devo dizer que esse SBCL realmente é um bicho parrudo.  Executou
(and (fatorial 100000) t) em 9,8 segundos.  Gambit, uma das mais
potentes implementações para Scheme, ficou na marca de 32 segundos
para uma implementação similar:

(define (fatorial n)
  (define (! n r) (if (< n 2) r (! (- n 1) (* r n))))
  (! n 1))

3 x mais lento...  por outro lado, gera C portável. :)

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Lisp-br" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/lisp-br?hl=en
-~----------~----~----~----~------~----~------~--~---

Responder a