Triángulo recursivo LISP

Oct 30 2020

Mi llamada recursiva parece no estar funcionando, estoy tratando de hacer un triángulo dado un número entero, y con la ayuda de ustedes finalmente pude hacer que la cadena se imprima en la misma línea, la cantidad correcta de veces para 1 línea. Al final de imprimir la línea, tengo una llamada recursiva que llama al triángulo para hacer otra línea, un carácter más corta. Esta llamada parece no ser alcanzada por alguna razón. Encuentre el código a continuación, gracias de antemano por cualquier y toda la ayuda, ¡todo es muy apreciado!

Nota: En una nota al margen, ¿hay alguna forma de detener una función en Lisp similar a una declaración de retorno? Me gustaría que la recursividad se detuviera en k = 1 y no continuara a k = 0.

(defun newTriangle (k) 
    (cond ((<= k 0) (princ '(Nope))) 
          ((or (= k 1) (= k -1)) (princ 'a)) 
          ((> k 0) (make-string k :initial-element #\a)) 
          (newTriangle (- k 1))))
        
(print (newTriangle 3))

triángulo de salida de muestra (3)

aaa 
aa 
a 

triángulo de salida de muestra (-3)

aaa 
 aa 
  a

Respuestas

3 adabsurdum Oct 30 2020 at 08:45

Primero, debe decidir exactamente qué es lo que newTriangleestá haciendo. La llamada (print (newTriangle 3))sugiere que newTriangledebería devolver una cadena , que luego es impresa por la llamada a print. Pero, la definición de OP newTrianglees tanto la salida de impresión como la devolución de una sola línea del triángulo como una cadena.

La llamada recursiva a newTrianglenunca se alcanza porque todos los casos posibles para el valor de kse agotan antes de que se alcance esta línea. Dado kque solo puede ser menor que cero, igual a cero o mayor que cero, y dado que todos estos casos se prueban antes de llegar a la llamada recursiva, nunca se alcanza. También tenga en cuenta que el código OP tiene la sintaxis incorrecta para la parte final de la conddeclaración. La primera expresión en una condrama es una prueba, y la convención es usar taquí para una rama que siempre se evaluará si se alcanza. Pero aquí no se necesitan tantos casos.

Suponiendo que la newTrianglefunción no debería devolver una cadena, pero debería imprimir un triángulo como efecto secundario , ¿qué debería hacer? Si el número de entrada es mayor que 0, debe imprimir una línea con un número de caracteres igual al número de entrada y luego llamarse a sí mismo con la entrada reducida en uno; de lo contrario, no debería hacer nada:

(defun print-triangle (k)
  (when (> k 0)
    (princ (make-string k :initial-element #\a))
    (terpri)
    (print-triangle (- k 1))))

Esta definición se nombra print-trianglepara enfatizar que imprime un triángulo como efecto secundario, y porque kebab-case es idiomático en Lisps, y camelCase no lo es. Tenga en cuenta que cada vez que print-trianglese llama con una entrada mayor que cero, se imprime una cadena de la longitud correcta y luego se imprime una nueva línea (con el nombre oscuro terpri, que solo escribe una nueva línea en el flujo de salida actual), antes de print-trianglevolver a llamar con kreducido en 1.

Ejemplo de interacción REPL:

CL-USER> (print-triangle 3)
aaa
aa
a
NIL

Si, en cambio, el objetivo es devolver una cadena, un enfoque sería llamar a una función auxiliar que mantenga el resultado en un parámetro:

(defun new-triangle (k)
  (build-triangle k ""))

(defun build-triangle (k result)
  (if (> k 0)
      (build-triangle (- k 1)
                      (concatenate 'string
                                   result
                                   (make-string k :initial-element #\a)
                                   (string #\newline)))
      result))

Aquí, new-triangletoma un argumento entero y llama build-triangle, pasando tanto el argumento entero como una cadena vacía en la resultposición. La build-trianglefunción funciona de la misma manera que print-triangleantes, pero en lugar de imprimir las líneas, se concatenan con result, junto con una cadena que contiene una nueva línea. Cuando build-trianglefinaliza, resultse vuelve a la cadena new-triangle. Tenga en cuenta que simplemente llamar new-triangledesde REPL imprimirá la cadena resultante como datos (es decir, entre comillas); llamar printal resultado de new-triangleimprimirá la cadena como datos y devolverá la cadena. Para ver la cadena impresa sin comillas, formatse puede utilizar; o puede usar princque imprimirá la cadena sin comillas y devolverá la cadena en sí:

CL-USER> (new-triangle 3)
"aaa
aa
a
"

CL-USER> (print (new-triangle 3))

"aaa
aa
a
" 
"aaa
aa
a
"

CL-USER> (format t "~A" (new-triangle 3))
aaa
aa
a
NIL

CL-USER> (princ (new-triangle 3))
aaa
aa
a
"aaa
aa
a
"