loops - 如何在 Common Lisp 循环中正确编写此条件?

标签 loops while-loop common-lisp

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

最佳答案

代码问题

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

关于loops - 如何在 Common Lisp 循环中正确编写此条件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50691305/

相关文章:

optimization - 查找质因数太慢或崩溃

set - 奇怪的 Common Lisp 交叉行为

java - 如何让用户结束这个程序?

java - 在哪里/如何在循环中创建新对象

php - 在 while 迭代的每个范围内获得不同结果的问题

C - 将字符串添加到现有数组

c# - 为什么我的 foreach 比 for 循环快?

C++ 如何检查多输入语句的第一个输入?

javascript - 使用 while 循环提醒数组

clojure - 在 Lisp 中,表单和文件是什么关系?