Lisp 收集随机物品,如果它们是偶数

问题描述 投票:0回答:2

来自其他编程语言,我最近发现了 lisp 并感到惊讶。通过

loop
宏阅读,我想生成一个带有条件随机数的列表(让我们假设数字应该是偶数)。所以我想出了这个:

(defun some-test (range)
  (loop repeat range
        with item = (random 1000)
        if (evenp item)
        collect item))

但是,这给了我相同的随机数

range
次。我在这里做错了什么?显然,
item
只被评估一次,而不是在每次迭代中都被评估。

lisp common-lisp
2个回答
4
投票

with
在循环开始之前计算表达式。 如果要在每次迭代中计算中间值,则需要将其替换为
for

(defun some-test (range)
  (loop for item = (random 1000)
        repeat range
        if (evenp item)
        collect item))

运行

(some-test 5)
然后返回最多5个偶数,例如:

CL-USER> (some-test 5)
(758 750 300)
CL-USER> (some-test 5)
(954)

3
投票

作为奖励,这里有一种方法可以准确地收集满足列表中测试的 N 个值。这是作为一个高阶函数完成的,它接受一个

size
参数,以及两个函数,
generate
test

(defun generate-list (size generate test)
  (when (> size 0)
    (loop for val = (funcall generate)
          if (funcall test val)
            collect val
            and count 1 into total
          until (= total size))))

LOOP 标准中描述的有一些最初可能不明显的限制,即不能以任何顺序在其正文中混合各种子句,子句的不同部分必须按顺序出现.这在 LOOPS for Black Belts - Putting It All Together 中得到了很好的总结。通常,循环的终止测试必须出现在迭代子句之后,这使得在循环开始时测试零限制变得很麻烦。我选择用

when
将这种情况排除在循环之外,当条件不满足时无论如何都会返回NIL。

或者,我可以使用

initially
子句,以便所有内容都包含在循环体中:

(defun generate-list (size generate test)
  (loop
    :initially
       (assert (>= size 0) (size) "Invalid size ~a" size)
       (when (= size 0)
         (return nil))
    :for val = (funcall generate)
    :if (funcall test val)
      :collect val
      :and :count 1 :into total
    :until (= total size)))

例如:

(defun make-dice (limit)
  (lambda ()
    (random limit)))

(generate-list 10 (make-dice 1000) #'evenp)
=> (714 728 964 510 898 404 560 850 48 508)
© www.soinside.com 2019 - 2024. All rights reserved.