2012-12-18 52 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_時建立的循環, –