Python的range()模拟在Common Lisp中

Mir*_*lov 29 python common-lisp number-sequence

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

换句话说,什么是相同的Python range在Common Lisp中的功能?

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

可以使用循环宏来模拟范围,但我想知道生成具有起点和终点以及步骤的数字序列的可接受方式.

相关:模拟Python在Scheme中的范围

Vat*_*ine 35

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

  • 使用 loop
  • 编写一个使用的实用程序函数 loop

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

(defun range (max &key (min 0) (step 1))
   (loop for n from min below max by step
      collect n))
Run Code Online (Sandbox Code Playgroud)

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

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

  • @copyninja您需要将其称为`(range 10:min 1)`或`(range 10:step 1)`。 (2认同)

小智 17

亚历山大港实施方案的iota:

(ql:quickload :alexandria)
(alexandria:iota 4 :start 2 :step 2)
;; (2 4 6 8)
Run Code Online (Sandbox Code Playgroud)


小智 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))
Run Code Online (Sandbox Code Playgroud)

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


好的,因为这里似乎有一个讨论.我假设真正需要的是类似于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
Run Code Online (Sandbox Code Playgroud)

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

  • 你已经实现了比OP要求的更多,并且以次优的方式.例如:函数'GENERATE'的名称是一个糟糕的选择.WITH-GENERATOR扩展为复杂的代码,然后通过LOOP扩展为更复杂的代码 - >在调试过程中可怕.您还会为每个生成的项目将闭包传递给另一个闭包.多么浪费. (3认同)
  • 这是值得深思的好食物。在幕后,Python 使用惰性/生成式风格实现 range,类似于 Clojure 的 range 函数。如果所需的用法是 1 到 10,循环就可以了。如果它是 1 到 100 亿,那么您可能应该使用惰性序列(就像 Python 确实那样),这在 Common Lisp 中当然是可能的,但没有标准化。 (2认同)

Mar*_*ark 5

使用递归:

(defun range (min max &optional (step 1))
  (when (<= min max)
    (cons min (range (+ min step) max step))))
Run Code Online (Sandbox Code Playgroud)