【发布时间】:2014-11-12 21:02:35
【问题描述】:
我目前正在研究“The Little Schemer”。我已经按照本书编写了一些函数,但我也想为它们编写一些单元测试。
我想创建一个对列表,每对包含一个带参数的函数,它是预期的输出。然后我将遍历这个列表并检查每个评估的函数是否与它的预期输出匹配。如果其中任何一个不匹配,则整个事物应返回 false。
这将通过代码示例更容易演示:
(define (test-fns list-of-tests)
; takes a list of pairs (functions and expected outputs) and returns true only
; if all outputs match expected
(define (test-fn test)
(let
([fn (car test)]
[expected (car (cdr test))])
(equal? (eval fn) expected))
)
(cond
((null? list-of-tests) #t)
(else
(and (test-fn (car list-of-tests)) (test-fns (cdr list-of-tests))))))
(let
([lat '(ice cream with fudge for dessert)]
[lat2 '(coffee cup tea cup and hick cup)])
(test-fns '(
((multisubst 'brown 'cup lat2) '(coffee brown tea brown and hick brown))
((multiinsertL 'brown 'cup lat2) '(coffee brown cup tea brown cup and hick brown cup))
((multiinsertR 'brown 'cup lat2) '(coffee cup brown tea cup brown and hick cup brown))
((multirember 'cup lat2) '(coffee tea and hick))
((subst 'topping 'fudge lat) '(ice cream with topping for dessert)))))
但是,这给了我一个错误:multisubst: unbound identifier;
我使用 DrRacket 逐步执行并意识到我正在传递符号 '(multisubst 'brown 'cup lat2),当然 lat2 没有在 test-fns 中定义。
所以我想在第二个代码块中以某种方式将每个局部变量(例如 lat 和 lat2)“扩展”到传递给 test-fns 的列表中的组件部分。
所以我们最终会用(multisubst 'brown 'cup '(coffee cup tea cup and hick cup)) 代替第一对的汽车。
我有一个模糊的概念,这可能是宏的用途,但我不确定。有什么想法吗?
--
V2
所以我按照之前的答案应用了 quasiquote,它起作用了(排序)。我简化了测试代码以使其更易于复制。
这是新代码:
(define (test-fns list-of-tests)
; takes a list of pairs (functions and expected outputs) and returns true only
; if all outputs match expected
(define (test-fn test)
(let
([fn (car test)]
[expected (car (cdr test))])
(equal? (eval fn) expected))
)
(cond
((null? list-of-tests) #t)
(else
(and (test-fn (car list-of-tests)) (test-fns (cdr list-of-tests))))))
(let
([lat '(coffee cup tea cup and hick cup)])
(test-fns `(
((identity (quote ,lat)) '(coffee cup tea cup and hick cup)))))
multisubst: unbound identifier; 仍然失败,但这一次我不知道为什么。在 DrRacket 调试器中,我们看到以下内容:
fn => (identity (quote (coffee cup tea cup and hick cup)))
expected => (quote (coffee cup tea cup and hick cup))
test => ((identity (quote (coffee cup tea cup and hick cup))) (quote (coffee cup tea cup and hick cup)))
list-of-tests => (((identity (quote (... ... ... ... ... ... ...))) (quote (coffee cup tea cup and hick cup))))
所以fn 实际上定义得很好。但是,如果我尝试直接使用 (fn) 对其进行评估,我会得到:
application: not a procedure;
expected a procedure that can be applied to arguments
given: '(identity '(coffee cup tea cup and hick cup))
arguments...: [none]
如果我尝试(eval fn),我会得到identity: unbound identifier; also, no #%app syntax transformer is bound in: identity
也许我的方法根本上是错误的,我应该有更好的方法来构建它?
--
V3 - 已解决!
好的,对于我的最后一次尝试,我简化了这种方式。我现在有这个:
(define (test-fns list-of-tests)
; takes a list of pairs (functions and expected outputs) and returns true only
; if all outputs match expected
(define (test-fn test)
(let
([fn (car test)]
[args (cadr test)]
[expected (caddr test)])
(equal? (fn args) expected))
)
(cond
((null? list-of-tests) #t)
(else
(and (test-fn (car list-of-tests)) (test-fns (cdr list-of-tests))))))
例子:
(test-fns (list (list identity '(coffee cup tea cup and hick cup) '(coffee cup tea cup and hick cup))))
> #t
(test-fns (list (list identity 1 0)))
> #f
并且,在混合中引入 quasiquote 允许我们在适当的位置扩展一个本地 lat 变量:
(let ([lat '(coffee cup tea cup and hick cup)])
(test-fns (list (list identity `,lat '(coffee cup tea cup and hick cup))
(list identity 0 0))))
> #t
耶!
--
V4 - 最终解决方案(我保证)
所以上面还有一个问题——我们只能为函数提供一个参数。最后的调整使用apply 和一个参数列表,如下所示:
;tests for chapter 3
(define (test-fns list-of-tests)
; takes a list of pairs (functions and expected outputs) and returns true only
; if all outputs match expected
(define (test-fn test)
(let
([fn (car test)]
[args (cadr test)]
[expected (caddr test)])
(equal? (apply fn args) expected))
)
(cond
((null? list-of-tests) #t)
(else
(and (test-fn (car list-of-tests)) (test-fns (cdr list-of-tests))))))
; tests for the tester
(let ([lat '(coffee cup tea cup and hick cup)])
(test-fns (list (list identity `(,lat) '(coffee cup tea cup and hick cup))
(list identity '(0) 0))))
; tests for chapter 3
(let
([lat '(ice cream with fudge for dessert)]
[lat2 '(coffee cup tea cup and hick cup)])
(test-fns (list (list multisubst `(brown cup ,lat2) '(coffee brown tea brown and hick brown)))))
输出
>#t
>#t
万岁,它有效!
--
V5 - 没有准引号
根据 LePetitPrince 在下面的回答,准引号实际上是完全没有必要的。我们可以直接调用 tester 传递标识符,如下所示:
; tests for the tester
(let ([lat '(coffee cup tea cup and hick cup)])
(test-fns (list (list identity (list lat) '(coffee cup tea cup and hick cup))
(list identity '(0) 0))))
; tests for chapter 3
(let
([lat '(ice cream with fudge for dessert)]
[lat2 '(coffee cup tea cup and hick cup)])
(test-fns (list (list multisubst (list 'brown 'cup lat2) '(coffee brown tea brown and hick brown)))))
输出:
>#t
>#t
【问题讨论】:
-
您可能正在寻找 quasiquote,但我不确定。不过,对我来说,这听起来很像宏。
-
您确定在这里需要准引用/取消引用吗?他们都互相抵消。只需使用
lat。 -
@LePetitPrince 你说的很对。让我修改我的答案。
标签: scheme