Definiendo una macro para iterar
Quería definir una nueva cláusula para la macro iterate. Algo similar a la función range de Python donde se tienen los parámetros start, stop, step
. Aquí está el primer intento:
(defmacro-clause (for var start start stop stop step step)
(if (minusp step)
`(for ,var from ,start downto ,stop by (- ,step))
`(for ,var from ,start to ,stop by ,step)))
Se encarga de trabajar con rangos crecientes y decrecientes utilizando las palabras clave to
y downto
de iterate. (A diferencia de Python, estos incluyen el valor stop
).
Esto funciona como se desea para:
(iter (for x start 5 stop 3 step -1)
(collect x))
Resultado: (5 4 3)
(iter (for x start 2 stop 5 step 1)
(collect x))
Resultado: (3, 4, 5)
Sin embargo, falla para algo como:
(let ((a 9)
(b 3)
(c -1))
(iter (for x start a stop b step c)
(collect x)))
¿Es una peculiaridad de iterate
que requiere números explícitos en estos lugares? No tiene problema con cosas como:
(iter (for x below (+ 3 3) by (+ 1 1))
(collect x))
Concretamente, mi pregunta es, ¿cómo puedo definir una nueva cláusula iterate
que acepte variables asignadas a números en estos lugares?
davy.ai
Desafortunadamente, no es posible utilizar variables vinculadas a números en las posiciones de
start
,stop
ystep
de la cláusulafor
en la macroiterate
. Esto se debe a que esas posiciones requieren literales en tiempo de expansión de macros, y las variables no se conocen hasta el tiempo de ejecución. Por lo tanto, no es una particularidad deiterate
, sino una limitación fundamental de las macros en general.Una posible solución alternativa es utilizar una macro separada para evaluar las variables y luego pasar sus valores a la macro
iterate
:Esto define una macro
my-range
que toma variables vinculadas a números y se expande en una formaloop
que genera la secuencia deseada. Luego podemos llamar amy-range
dentro deiterate
para generar la secuencia e iterar sobre ella como de costumbre. Ten en cuenta que esta implementación asume que las variables están vinculadas a secuencias ascendentes; si necesitas manejar secuencias descendentes también, deberás modificarmy-range
en consecuencia.Otra posible solución alternativa es utilizar una función en lugar de una macro para la cláusula
for
:Esto define una función
my-for
que toma variables vinculadas a números y devuelve la cláusulafor
correspondiente como una lista. Luego podemos llamar amy-for
dentro deiterate
para generar la cláusulafor
de manera dinámica en función de los valores de las variables. Este enfoque tiene la ventaja de ser más flexible que la macromy-range
, ya que puede manejar todo tipo de cláusulasfor
(no sólo rangos). Sin embargo, tiene la desventaja de ser más lento que una macro, ya que implica llamadas de función en tiempo de ejecución y construcción de listas.En general, aunque no es posible definir una cláusula
for
que acepte variables vinculadas a números directamente eniterate
, existen varias soluciones alternativas que pueden lograr efectos similares si es necesario.