2

Common Lisp.

I'm trying to determine if a string is present in a given list.

My goal is to have (member searched-string my-list) but I keep getting NIL.

Why does (member "foo" '("foo" "bar")) return NIL?

enter image description here

Will Ness
  • 70,110
  • 9
  • 98
  • 181

2 Answers2

8

The default value for the :test keyword parameter is #'eql:

If neither a :test nor a :test-not argument is supplied, it is as if a :test argument of #'eql was supplied.

17.2.1 Rules about Test Functions - Satisfying a Two-Argument Test

Strings are not numbers nor characters, so two strings are eql only if they are eq (identical), but in your example you probably have two different strings allocated. You could have optimizations where string= strings are interned by the compiler, making them eq, but that would be an implementation detail.

Here below, the same foo string is used to build the list, and as an argument to member, and the call actually find the value:

(let ((foo "foo"))
  (member foo (list foo "bar")))
=> ("foo" "bar")

But more generally, you want to pass a string comparison function, like string-equal (case-insensitive) or string= (exact case), or simply a general-purpose equality predicate; both examples below find the sarch string:

(member "foo" '("foo" "bar") :test #'equal)
(member "FOO" '("foo" "bar") :test #'equalp)
coredump
  • 37,664
  • 5
  • 43
  • 77
  • how would one find this, in CLHS? http://clhs.lisp.se/Body/f_mem_m.htm says nothing about this issue that I could find; it calls its argument "list", not "sequence"; `member` does not even appear in http://clhs.lisp.se/Body/c_sequen.htm... – Will Ness May 08 '20 at 13:46
  • I thought it would be linked from MEMBER, but there was no such link; then I look at the page for [FIND](http://clhs.lisp.se/Body/f_find_.htm#find) and it was there, at the bottom. Yeah not very easy to find. – coredump May 08 '20 at 14:14
4

It's because (eql "foo" "foo") ; ==> nil. While not documented in the CLHS member uses #'eql as the standard test. To get it to use #'equal that also evaluates t for structures that display the same

(equal "foo" "foo") ; ==> t
(member "foo" '("foo" "bar") :test #'equal) ; ==> ("foo" "bar")

A string is stored as an vector of characters and thus unless it is the same object eq and eql will evaluate to nil while equal checks if all the characters in the strings are eql.

NB: (eql "foo" "foo") can also be t. Since it is literals and thus constant data the same code compiled will in some implementation only store "test" once so that they become pointer equal (eq).

Sylwester
  • 47,942
  • 4
  • 47
  • 79