简体   繁体   English

lisp中破坏性函数的单元测试

[英]Unit testing of destructive functions in lisp

The background here is that I have a non-destructive version of a function that I want to make destructive for performance reasons.这里的背景是我有一个 function 的非破坏性版本,出于性能原因我想使其具有破坏性。 However, writing unit tests gets challenging because lisp considers all quoted lists in the source code to be constants and changing those leads to undefined behaviour.然而,编写单元测试变得具有挑战性,因为 lisp 将源代码中的所有引用列表视为常量,更改这些会导致未定义的行为。

I'm using parachute as the unit testing framework我使用降落伞作为单元测试框架

eg,例如,

(define-test suite-1
 (let ((start '((1 2) (3 4) (5 6)))
       (end '((7 1 2) (3 4) (5 6))))
  (is #'equal end (push 7 (car start))))
 (let ((start '((1 2) (3 4) (5 6)))
       (end '((8 1 2) (3 4) (5 6))))
  (is #'equal end (push 8 (car start)))))

This kinda fails because we end up pushing 8 onto the constant list (1 2) which eventually causes (push 8 (car start)) to result in (8 7 1 2) instead of the expected (8 1 2)这有点失败,因为我们最终将8推入常量列表(1 2)最终导致(push 8 (car start))导致(8 7 1 2)而不是预期的(8 1 2)

This isn't a problem with testing non-destructive functions because they don't modify constants.这不是测试非破坏性函数的问题,因为它们不修改常量。 This is also not a problem outside of unit tests because I know that the original structure will no longer be needed.这在单元测试之外也不是问题,因为我知道将不再需要原始结构。

I could replace the above with this ugly thing:-我可以用这个丑陋的东西代替上面的东西:-

(let ((start (list (list 1 2) (list 3 4) (list 5 6))) ...

which then creates a proper non-constant list but it sure does make the code unreadable...然后创建一个适当的非常量列表,但它确实使代码不可读......

Any suggestions on how other people approach this?关于其他人如何处理这个问题的任何建议?

Use COPY-TREE to make a deep copy of the quoted list structure.使用COPY-TREE对引用列表结构进行深度复制。

(let ((start (copy-tree '((1 2) (3 4) (5 6)))))
  ...
)

Your example has several mistakes.你的例子有几个错误。

(define-test suite-1
 (let ((start '((1 2) (3 4) (5 6)))
       (end '((7 1 2) (3 4) (5 6))))
  (is #'equal end (push 7 (car start))))
 (let ((start '((1 2) (3 4) (5 6)))
       (end '((8 1 2) (3 4) (5 6))))
  (is #'equal end (push 8 (car start)))))

These are two independent let expressions.这是两个独立的let表达式。 Meaning, the start in the first expression - no matter what destructive function you apply on it - can't affect the start in the second expression.意思是,第一个表达式的start - 无论您对其应用什么破坏性的 function - 都不会影响第二个表达式的start Since inbetween, it got newly created using the same quoted list expression.从那以后,它是使用相同的引用列表表达式新创建的。

Second, (push 7 (car start)) doesn't return the entire start content, thus just it returns only the modified car of start .其次, (push 7 (car start))不返回整个start内容,因此它只返回start的修改后的car Therefore, you can't compare it with end but you should compare it to (car end) for equal ity.所以不能和end比较,应该和(car end)比较,看是否equal If you do that, your code should run through.如果你这样做,你的代码应该运行。 I tested it by:我通过以下方式对其进行了测试:

  (defun test ()
    (let ((start '((1 2) (3 4) (5 6)))
          (end '((7 1 2) (3 4) (5 6))))
      (assert (equal (car end) (push 7 (car start)))))
    (let ((start '((1 2) (3 4) (5 6)))
          (end '((8 1 2) (3 4) (5 6))))
      (assert (equal (car end) (push 8 (car start)))))
    (print "successfully run through."))
;; and then run:
(test)
;; 
;; "successfully run through." 
'' "successfully run through."

So the assert s were fulfilled.所以assert被满足了。

SBCL warns about applying destructive function on constant data. SBCL 警告对常量数据应用破坏性 function。 But the constant data applies only wihin the let expression you are using.但是常量数据仅适用于您正在使用的let表达式。 But outside of it, in a new let expression, a '((1 2) (3 4) (5 6)) would produce the same list always.但在它之外,在一个新的let表达式中,一个'((1 2) (3 4) (5 6))总是会产生相同的列表。

I couldn't believe when I saw your example that - if you would use (car end) to test for equal ity, that it would give in the second expression anything else than T .当我看到您的示例时,我简直不敢相信 - 如果您使用(car end)来测试equal性,它会在第二个表达式中给出除T之外的任何其他内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM