【问题标题】:How do I properly write this conditionals in a Loop for Common Lisp?如何在 Common Lisp 的循环中正确编写此条件?
【发布时间】:2018-11-14 10:07:43
【问题描述】:
(defun lista-n (a b c)   
  (loop repeat 10
        for x = (+ a c) then (+ x c)                            
                             (while (/= x a) 
                                    do (if (> x b) 
                                       (- x b))   ;then               
                             collect x))

我是 Common Lisp 的新手,我需要知道这个循环的正确语法。

我希望能够获得一个循环列表,例如 (lista-n 0 5 2) => (0 2 4 1 3 5)

0 到 5 之间的列表,乘以 2。如果 Number > 5,则 Number - 5。

【问题讨论】:

  • 您可能希望通过缩进代码来提高可读性。

标签: loops while-loop common-lisp


【解决方案1】:

代码问题

与 Common Lisp 中的大多数结构相反,LOOP 的语法故意使用了少量括号。 (while ...) 部分在这种情况下不合适。此外,您可以使用until (= x a),我觉得它更具可读性。看看§22. LOOP for Black Belts

另外,(- x b) 只计算减法,但不影响任何变量。如果你想减少x,比如C语言中的x -= b,使用(decf x b)

您的函数和变量的名称也无助于理解应该发生的事情。

最后,如果您的步长很大,您的代码可能无法正常运行,因为仅计算 (- x b) 可能会得到仍然大于 b 的结果。此外,负输入可能存在问题。

第一次尝试

我试图处理我能想到的所有极端情况,比如负步骤等。还有一个测试通过检查当前数字是否已经存在于列表中来防止无限循环。检查在时间上是线性的,这使得整个循环是二次的。对于非常大的列表,这可能是 问题。

(defun circular-range (from to step)
  (loop
     with low = (min from to) and high = (max from to)
     with divisor = (- high low)
     for value = from then (+ wrapped step)
     for wrapped = (if (<= low value high) 
                       value
                       (+ low (mod (- value low) divisor)))
     until (member wrapped numbers)
     collect wrapped into numbers
     until (= wrapped to)
     finally (return numbers)))

使用更多数学

借助数学,可以从所覆盖的范围和步长中知道周期的大小:Progressions modulo n。这允许删除一些检查,特别是已经看到的数字列表。

(defun circular-range (from to step)
  (loop
     with low = (min from to) and high = (max from to)
     with range = (- high low)
     with period = (/ range (gcd range step))
     repeat (1+ period)
     for value = from then (+ wrapped step)
     for first = t then nil
     for wrapped = (if (<= low value high) 
                       value
                       (+ low (mod (- value low) range)))
     when (or first (/= wrapped from))
       collect wrapped))

然而,我们需要再次重复以满足规范并收集to 值,除非该值等于from

测试

以下结果与两个版本相同。

(circular-range 0 5 0)
=> (0)

(circular-range 0 5 2)
=> (0 2 4 1 3 5)

(circular-range 0 -5 2)
=> (0 -3 -1 -4 -2)

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

(circular-range 10 50 13)
=> (10 23 36 49 22 35 48 21 34 47 20 33 46 19 32 45 18 31 44 17 30 43 16 29 42 15
   28 41 14 27 40 13 26 39 12 25 38 11 24 37 50)

(circular-range 30 35 -2)
=> (30 33 31 34 32)

(circular-range 30 30 -5)
=> (30)

【讨论】:

  • 当 WITH 子句和 FOR 子句独立时,可以用 AND 标记: FOR a from b to c and d from to f... WITH foo = 1 and bar = 2 .. ..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-11-16
  • 2013-05-16
  • 1970-01-01
  • 2011-03-08
  • 1970-01-01
  • 1970-01-01
  • 2021-09-12
相关资源
最近更新 更多