python common-lisp number-sequence

Rango de Python() analógico en Common Lisp



common-lisp number-sequence (7)

Al no encontrar lo que quería ni querer usar un paquete externo, terminé escribiendo mi propia versión que difiere de la versión de python (con la esperanza de que mejore) y evita el bucle. Si crees que es realmente ineficiente y puede mejorarlo, por favor hazlo.

;; A version of range taking the form (range [[first] last [[step]]]). ;; It takes negative numbers and corrects STEP to the same direction ;; as FIRST to LAST then returns a list starting from FIRST and ;; ending before LAST (defun range (&rest args) (case (length args) ( (0) ''()) ( (1) (range 0 (car args) (if (minusp (car args)) -1 1))) ( (2) (range (car args) (cadr args) (if (>= (car args) (cadr args)) -1 1))) ( (3) (let* ((start (car args)) (end (cadr args)) (step (abs (caddr args)))) (if (>= end start) (do ((i start (+ i step)) (acc ''() (push i acc))) ((>= i end) (nreverse acc))) (do ((i start (- i step)) (acc ''() (push i acc))) ((<= i end) (nreverse acc)))))) (t (error "ERROR, too many arguments for range")))) ;; (range-inc [[first] last [[step]]] ) includes LAST in the returned range (defun range-inc (&rest args) (case (length args) ( (0) ''()) ( (1) (append (range (car args)) args)) ( (2) (append (range (car args) (cadr args)) (cdr args))) ( (3) (append (range (car args) (cadr args) (caddr args)) (list (cadr args)))) (t (error "ERROR, too many arguments for range-inc"))))

Nota: también escribí una versión de esquema

¿Cómo crear una lista de números consecutivos en Common Lisp?

En otras palabras, ¿cuál es el equivalente de la función de range de Python en Common Lisp?

En el range(2, 10, 2) Python range(2, 10, 2) devuelve [2, 4, 6, 8] , con el primer y último argumento como opcionales. No pude encontrar la forma idiomática de crear una secuencia de números, aunque Emacs Lisp tiene number-sequence .

El rango se puede emular usando una macro de bucle , pero quiero saber la forma aceptada de generar una secuencia de números con puntos de inicio y final y paso.

Relacionados: Analog of Python''s range in Scheme.


Así es como abordaría el problema:

(defun generate (from to &optional (by 1)) #''(lambda (f) (when (< from to) (prog1 (or (funcall f from) t) (incf from by))))) (defmacro with-generator ((var from to &optional (by 1)) &body body) (let ((generator (gensym))) `(loop with ,generator = (generate ,from ,to ,by) while (funcall ,generator #''(lambda (,var) ,@body))))) (with-generator (i 1 10) (format t "~&i = ~s" i))

Pero esto es solo una idea general, hay mucho margen de mejora.

OK, ya que parece que hay una discusión aquí. He asumido que lo que realmente se necesita es el análogo a la función del generador de range de Python. Lo que, en cierto sentido, genera una lista de números, pero lo hace generando un número en cada iteración (para que no cree más de un elemento a la vez). Los generadores son un concepto un tanto raro (pocos lenguajes lo implementan), así que asumí que la mención de Python sugería que esta característica exacta es deseada.

Siguiendo algunas críticas de mi ejemplo anterior, aquí hay un ejemplo diferente que ilustra la razón por la cual un generador podría usarse en lugar de un simple bucle.

(defun generate (from to &optional (by 1)) #''(lambda () (when (< from to) (prog1 from (incf from by))))) (defmacro with-generator ((var generator &optional (exit-condition t)) &body body) (let ((g (gensym))) `(do ((,g ,generator)) (nil) (let ((,var (funcall ,g))) (when (or (null ,var) ,exit-condition) (return ,g)) ,@body)))) (let ((gen (with-generator (i (generate 1 10) (> i 4)) (format t "~&i = ~s" i)))) (format t "~&in the middle") (with-generator (j gen (> j 7)) (format t "~&j = ~s" j))) ;; i = 1 ;; i = 2 ;; i = 3 ;; i = 4 ;; in the middle ;; j = 6 ;; j = 7

Esto es, nuevamente, solo una ilustración del propósito de esta función. Probablemente sea un desperdicio usarlo para generar enteros, incluso si necesita hacerlo en dos pasos, pero los generadores son mejores con los analizadores, cuando se desea generar un objeto más complejo que se construye según el estado anterior del analizador. por ejemplo, y un montón de otras cosas. Bueno, puedes leer un argumento al respecto aquí: http://en.wikipedia.org/wiki/Generator_%28computer_programming%29


En forma simple especificando start, stop, step:

(defun range (start stop step) (do ( (i start (+ i step)) (acc ''() (push i acc))) ((>= i stop) (nreverse acc))))


Es posible que desee probar snakes :

"Generadores de estilo Python para Common Lisp. Incluye un puerto de itertools".

Está disponible en Quicklisp . Puede haber otras bibliotecas de Common Lisp que pueden ayudar.


No hay una forma integrada de generar una secuencia de números, la forma canónica de hacerlo es hacer una de las siguientes acciones:

  • Usar loop
  • Escribe una función de utilidad que usa loop

Una implementación de ejemplo sería (esto solo acepta contar desde "bajo" a "alto"):

(defun range (max &key (min 0) (step 1)) (loop for n from min below max by step collect n))

Esto le permite especificar un valor mínimo (opcional) y un valor de paso (opcional).

Para generar números impares: (range 10 :min 1 :step 2)


Utilizando la recursividad:

(defun range (min max &optional (step 1)) (when (<= min max) (cons min (range (+ min step) max step))))


alexandria implementa la iota del esquema:

(ql:quickload :alexandria) (alexandria:iota 4 :start 2 :step 2) ;; (2 4 6 8)