Lisp如果它们是偶数,收集随机项目

huangapple go评论74阅读模式
英文:

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)

huangapple
  • 本文由 发表于 2023年3月3日 21:56:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/75627991.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定