英文:
Lisp collect random items if they are even
问题
来自其他编程语言,我最近发现了 Lisp,感到非常惊讶。阅读 loop
宏后,我想生成一个带有条件的随机数列表(假装这些数字应该是偶数)。所以我想到了这个:
(defun some-test (range)
(loop repeat range
with item = (random 1000)
if (evenp item)
collect item))
然而,这给我提供了相同的随机数,重复了 range
次。我在这里做错了什么?显然,item
只被评估一次,而不是在每次迭代中。
英文:
Coming from other programming languages, I recently found lisp and am amazed. Reading through the loop
macro, I wanted to generate a list with random numbers with a condition (let's just pretend the numbers should be even). So I came up with this:
(defun some-test (range)
(loop repeat range
with item = (random 1000)
if (evenp item)
collect item))
However, this gives me the same random number range
times. What am I doing wrong here? Apparently, item
is only evaluated once and not in every iteration.
答案1
得分: 5
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)
英文:
with
evaluates the expression before the loop starts.
If you want to compute an intermediate value on each iteration, you need to replace it with for
:
(defun some-test (range)
(loop for item = (random 1000)
repeat range
if (evenp item)
collect item))
Running (some-test 5)
then returns up to 5 even numbers, for example:
CL-USER> (some-test 5)
(758 750 300)
CL-USER> (some-test 5)
(954)
答案2
得分: 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)
英文:
As a bonus, here is a way of collecting exactly N values satisfying a test in a list. This is done as a higher-order function that accepts a size
parameter, as well as two functions, generate
and 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 as described in the standard has certain restrictions that are maybe not apparent at first, namely that it is not possible to mix all kinds of clauses in its body in any order, there are different sections of clauses that must appear in sequence. This is summarized nicely in LOOPS for Black Belts - Putting It All Together. Typically the termination test of the loop must appear after the iteration clauses, making it cumbersome to test for a limit of zero at the beginning of the loop. I choose to exclude this case outside of loop with when
, which returns NIL anyway when the condition is not met.
Alternatively, I can use an initially
clause so that everything is contained in the loop body:
(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)))
For example:
(defun make-dice (limit)
(lambda ()
(random limit)))
(generate-list 10 (make-dice 1000) #'evenp)
=> (714 728 964 510 898 404 560 850 48 508)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论