list recursion binary lisp

De decimal a binario en Lisp: crea una lista no anidada



recursion binary (2)

Al llegar a mis casos de recursividad, uso list para anexar el resultado futuro con el actual, pero termino con una lista anidada debido a la recursión. Esto causa un error cuando tengo un número que causa recursión por más de cinco veces.

Alguna idea de cómo puedo obtener resultados en una sola lista simple no anidada, por ejemplo:

CL-USER 100: 8> (BINARY_LIST 4)

(1 0 0)

Código y resultado de ejemplo:

CL-USER 99 : 8 > (defun binary_list (i) (COND ((= i 0) 0) ((= i 1) 1) ((= (mod i 2) 0) (list (binary_list (truncate i 2)) 0)) (t (list (binary_list (truncate i 2)) 1)) ) ) BINARY_LIST CL-USER 100 : 8 > (BINARY_LIST 4) ((1 0) 0) CL-USER 101 : 8 > (BINARY_LIST 104) ((((# 1) 0) 0) 0)


Ya casi has llegado. Todo lo que necesita hacer es reemplazar la list con nconc :

(defun binary-list (n) (cond ((= n 0) (list 0)) ((= n 1) (list 1)) (t (nconc (binary-list (truncate n 2)) (list (mod n 2))))))

Puede evitar llamar tanto truncate como mod recogiendo ambos valores en la división de enteros:

(defun binary-list (n) (assert (>= n 0)) (multiple-value-bind (q r) (floor n 2) (if (zerop q) (list r) (nconc (binary-list q) (list r)))))

Tenga en cuenta que este algoritmo es cuadrático porque nconc tiene que atravesar el resultado en cada iteración. Esto se puede evitar pasando un acumulador:

(defun binary-list (n &optional acc) (assert (>= n 0)) (multiple-value-bind (q r) (floor n 2) (if (zerop q) (cons r acc) (binary-list q (cons r acc)))))

Ahora tenemos una función recursiva de cola que puede ser compilada a la iteración por un compilador moderno.

Un truco de optimización más que podría usar (que, de hecho, debería ser hecho por el compilador - ¡intente disassemble para verificar!) logand ash y logand lugar del floor mucho más general y caro:

(defun binary-list (n &optional acc) (cond ((zerop n) (or acc (list 0))) ((plusp n) (binary-list (ash n -1) (cons (logand 1 n) acc))) (t (error "~S: non-negative argument required, got ~s" ''binary-list n))))

Por cierto, los lispers usualmente utilizan dash en lugar de guiones bajos en símbolos, por lo que tu binary_list debería ser binary-list si no quieres ofender nuestra tierna estética.


me parece que es la manera más directa y menos indirecta de lograr los resultados deseados siempre:

(defun mvp-binary-from-decimal (n r) (if (zerop n) r (multiple-value-bind (a b) (floor n 2) (mvp-binary-from-decimal a (cons b r))))) (defun binary-from-decimal (n) (if (and (numberp n) (plusp n)) (mvp-binary-from-decimal n ''()) (if (eql n 0) ''(0) nil)))

probado en limo, sbcl, clisp - usado de la siguiente manera:

CL-USER> (binary-from-decimal 100) (1 1 0 0 1 0 0) CL-USER> (binary-from-decimal 10) (1 0 1 0) CL-USER> (binary-from-decimal 0) (0)

Hay algunas razones avanzadas en cuanto a por qué esta podría ser la manera más deseable para implementar dicha funcionalidad, pero por ahora, basta decir que es limpia, educada, legible y siempre funciona.