2012-12-18 53 views
25

如何在Common Lisp中创建连续数字列表?Python的范围()模拟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

+0

我使用'delay'和'force'实现了类似的东西。这模仿Python的'范围'。 – sean

+0

@sean'xrange'肯定? – Vatine

+0

@Vatine,True,但也取决于python版本当然。 – sean

回答

28

还有就是生成数字的顺序没有内置的方式,这样做的规范的方法是做一个:

  • 使用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)

+0

我试过这个函数,如果我使用(范围10)它可以工作,但是当我尝试时(范围10 1),我在SBCL中得到一个回溯,它说'奇数个&KEY参数 [SB-INT类型的条件:SIMPLE-PROGRAM -ERROR]'如果我尝试(范围10 1 1),我得到'未知&KEY参数:1 [SB-INT类型的条件:简单程序错误]'我刚开始用CL可以让我指出一点我在这里做错了什么?我正在使用SBCL – copyninja

+1

@copyninja您需要将它称为“(范围10:分钟1)”或“(范围10:步骤1)”。 – Vatine

+0

非常感谢我没有意识到使用&key :-) – copyninja

4

以下是我会处理这个问题:

(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

+1

目前还不清楚你赢了什么(循环为1从1下10乘1 ...)。 –

+3

您已经实施了比OP所要求的更多且次优的方式。例如:函数'GENERATE'的名字是一个糟糕的选择。 WITH-GENERATOR扩展成复杂的代码,然后通过LOOP扩展成更复杂的代码 - >在调试过程中可怕。您还会为每个生成的项目传递一个闭包到另一个闭包。真是浪费。 –

+0

@wvxvw:取决于OP是否需要生成器范围或非生成器范围... –

16

亚历山大实施方案的丝毫:

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

谢谢。这是做到这一点的正确方法! –

+0

指出[cl21](http://cl21.org/)库将其与其他一些相关结构('map-iota','take',...)集成在一起。 – Ehvince

3

使用递归:

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

在简单的形式指定的启动,停止,步:

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

使用+1代替循环。就我而言,我还没有克服我在阅读格雷厄姆的_ANSI Common Lisp_时建立的循环, –