为何在fiveam测试环境中调用`member`函数表现异常?
问题原因分析
你遇到的差异本质是字符串字面量的对象同一性问题,结合Common Lisp的eql判定规则和FiveAM的编译优化导致:
核心规则
eql对字符串的判定逻辑是:只有当两个字符串是内存中的同一个对象时才返回真,而非比较字符串内容。如果需要判断字符串内容是否匹配,应该用equal或string=。
两种环境的差异
普通Common Lisp解释器会话:
解释器通常不会对字符串字面量做池化优化,代码里的两个"foo"是独立的内存对象(地址不同)。因此(eql "foo" "foo")返回NIL,member找不到匹配项,最终返回NIL。FiveAM测试环境:
FiveAM运行测试时会编译执行代码,且默认启用了字符串字面量共享优化——编译时会把相同内容的字符串字面量合并为同一个内存对象。此时测试代码里的两个"foo"是同一个对象,(eql "foo" "foo")返回T,member找到匹配项,返回("foo")。
验证与解决
- 验证:在两种环境下分别执行
(eq "foo" "foo"),普通环境返回NIL,FiveAM环境返回T(eq是eql的子集,对字符串判定逻辑一致)。 - 解决:如果需求是判断字符串内容是否存在,将测试函数换成
#'equal或#'string=即可:(member "foo" '("foo") :test #'equal)
复现代码
test.lisp
#!/usr/bin/env -S sbcl --script (load "~/.quicklisp/setup") (require :asdf) (ql:quickload "fiveam" :silent t) (load "mre.asd") (asdf:load-system :mre) (asdf:test-system :mre) (format t "~&(member \"foo\" '(\"foo\") :test #'eql) evaluates to: ~A~%" (member "foo" '("foo") :test #'eql))
mre.asd
(asdf:defsystem "mre" :depends-on ("fiveam") :components ((:file "mre")) :perform (asdf:test-op (o c) (uiop:symbol-call :fiveam :run! (find-symbol* '#:mre-tests :mre))))
mre.lisp
(defpackage :mre (:use :cl :fiveam) (:export :utils-sets-hasduplicatesp)) (in-package :mre) (def-suite mre-tests) (in-suite mre-tests) (test member-test (is-false (member "foo" '("foo") :test #'eql)))
运行结果
$ ./test.lisp ./test.lisp Running test suite MRE-TESTS Running test MEMBER-TEST f Did 1 check. Pass: 0 ( 0%) Skip: 0 ( 0%) Fail: 1 (100%) Failure Details: -------------------------------- MEMBER-TEST in MRE-TESTS []: (MEMBER "foo" '("foo") :TEST #'EQL) returned the value ("foo"), which is true -------------------------------- (member "foo" '("foo") :test #'eql) evaluates to: NIL
内容的提问来源于stack exchange,提问作者vmonteco
相关产品推荐
相关产品推荐

