Python的range()模拟在Common Lisp中

时间:2012-12-18 16:39:42

标签: python common-lisp number-sequence

如何在Common Lisp中创建连续数字列表?

换句话说,Common Lisp中Python的range函数的等价物是什么?

在Python range(2, 10, 2)中返回[2, 4, 6, 8],第一个和最后一个参数是可选的。虽然Emacs Lisp有number-sequence,但我找不到创建数字序列的惯用方法。

范围可以模拟using loop macro,但我想知道生成一系列包含起点和终点以及步骤的数字的可接受方式。

相关:Analog of Python's range in Scheme

10 个答案:

答案 0 :(得分:35)

没有内置的生成数字序列的方法,这样做的规范方法是执行以下操作之一:

  • 使用loop
  • 编写使用loop
  • 的实用程序功能

一个示例实现是(这只接受“从低”到“高”的计数):

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

这允许您指定(可选)最小值和(可选)步长值。

生成奇数:(range 10 :min 1 :step 2)

答案 1 :(得分:17)

alexandria实现了方案的iota:

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

答案 2 :(得分:5)

以下是我解决问题的方法:

(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))

但这只是一般性的想法,还有很大的改进空间。


好的,因为这里似乎有一个讨论。我假设真正需要的是类似于Python的range生成器函数。在某种意义上,它会生成一个数字列表,但通过每次迭代产生一个数字来做到这一点(因此它不会一次创建多个项目)。生成器是一个有点罕见的概念(很少有语言实现它),所以我认为提到Python表明需要这个确切的特性。

在对上面的例子进行一些批评后,这里有一个不同的例子,说明为什么可以使用生成器而不是简单的循环。

(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

这再一次只是这个功能的目的的说明。使用它来生成整数可能是浪费的,即使你需要分两步完成,但生成器最适合使用解析器,当你想要产生一个更复杂的对象时,它是根据解析器的先前状态构建的,例如,还有很多其他的东西。那么,你可以在这里阅读一个关于它的论点:http://en.wikipedia.org/wiki/Generator_%28computer_programming%29

答案 3 :(得分:3)

使用递归:

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

答案 4 :(得分:1)

以简单的形式指定开始,停止,步骤:

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

答案 5 :(得分:1)

没有找到我想要的东西,也不想使用外部软件包,我最终写了自己的版本,该版本不同于python版本(希望对其进行改进)并避免循环。如果您认为它确实效率低下并且可以改进它,请这样做。

;; 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"))))

注意:我也写了scheme version

答案 6 :(得分:0)

您可以尝试snakes

&#34; Common Lisp的Python样式生成器。包括一个itertools端口。&#34;

可在Quicklisp中找到。可能有其他Common Lisp库可以提供帮助。

答案 7 :(得分:0)

这是一个范围函数,用于生成数字列表。 我们使用 do “循环”。如果存在功能循环之类的问题,那么它就是 do 宏。尽管没有递归,但是当您构造 do 时,我发现思路很相似。您在 do 中考虑每个变量的方式与您在递归调用中考虑每个参数的方式相同。

我使用列表* 而不是缺点列表* 缺点完全相同,但可以有1个,2个或更多参数。 (列出1 2 3 4无)(缺点1(缺点2(缺点3(缺点4无))))

(defun range (from-n to-n &optional (step 1)) ; step defaults to 1
  (do ((n from-n (+ n step))        ; n initializes to from-n, increments by step
       (lst nil (list* n lst)))     ; n "pushed" or "prepended" to lst

      ((> n to-n)                   ; the "recursion" termination condition
       (reverse lst))))             ; prepending with list* more efficient than using append
                                    ; however, need extra step to reverse lst so that
                                    ; numbers are in order

这是一个测试环节:

  

CL-USER 23>(范围0 10)

     

(0 1 2 3 4 5 6 7 8 9 10)

     

CL-USER 24>(范围10 0 -1)

     

     

CL-USER 25>(范围10 0 1)

     

     

CL-USER 26>(范围1 21 2)

     

(1 3 5 7 9 11 13 15 17 19 21)

     

CL-USER 27>(反向(范围1 21 2))

     

(21 19 17 15 13 11 9 7 5 3 1)

     

CL用户28>

此版本不适用于减少序列。但是,您看到可以使用反向来减小序列。

答案 8 :(得分:0)

需要在(range n)dotimes可用的tiny Lisp中实现setq

(defun range (&rest args)
    (let ( (to '()) )
        (cond 
            ((= (length args) 1) (dotimes (i (car args))
                (push i to)))
            ((= (length args) 2) (dotimes (i (- (cadr args) (car args)))
                (push (+ i (car args)) to))))
    (nreverse to)))

示例:

> (range 10)
(0 1 2 3 4 5 6 7 8 9)

> (range 10 15)
(10 11 12 13 14)

答案 9 :(得分:0)

以防万一,这类似于user1969453的答案,它返回一个向量而不是一个列表:

(defun seq (from to &optional (step 1))
  (do ((acc (make-array 1 :adjustable t :fill-pointer 0))
       (i from (+ i step)))
      ((> i to) acc) (vector-push-extend i acc)))

或者,如果您要预分配向量,并跳过“向量推”习惯用法:

(defun seq2 (from to &optional (step 1))
  (let ((size (+ 1 (floor (/ (- to from) step))))) ; size is 1 + floor((to - from) / step))
    (do ((acc (make-array size))
         (i from (+ i step))
         (count 0 (1+ count)))
        ((> i to) acc) (setf (aref acc count) i))))