4
\$\begingroup\$

I pretty sure it should be shoter solution.

(defun modulename-vinlist(val lst)
  "Check if value in list"
  (let ((res "no"))
    (let (vv)
      (dolist (pi lst vv)
        (when (string-equal pi val)
          (setq res "yes")
          (return))
        (setq vv (cons pi vv))))
    (when (string-equal res "yes")
      t)))
\$\endgroup\$

2 Answers 2

6
\$\begingroup\$

You want member:

(member val lst)

For example

(if (member 3 '(1 2 3)) t nil) ; t
(if (member 4 '(1 2 3)) t nil) ; nil
\$\endgroup\$
1
  • \$\begingroup\$ In the examples, the if structure is redundant. The value '(3) returned in the first example is true. The only difference is that '(3) will not return t when passed to booleanp...but since nil will return t when passed to booleanp that's probably not a good reason to add the if control structure. \$\endgroup\$ Commented Jan 26, 2017 at 16:48
2
\$\begingroup\$

The Good Thing

The code works and does what was intended. Making it shorter and improving readability are improvements not bug fixes.

Readability

It is hard to follow the logic of the code. Adding a documentation string and comments would assist a future programmer (which might be the program's author in a few months) in figuring out what is going on and guide the programmer in making changes.

Names The name modulename-vinlist does not follow traditional naming conventions for Lisp programs. Hyphen case is more typical module-name-v-in-list would be more conventional. I'd probably favor adding a few extra keystrokes for legibility with module-name-value-in-list because it is more readable and often spend longer reading code than writing it.

Philosophy

As noted in the accepted answer Emacs Lisp provides the member function as a simple way of determining if a value is in a list. However, it is possible to mean different things by 'value' depending on context. Emacs Lisp addresses this with additional functions such as memq, memql etc. to provide more stringent set semantics for lists. Emacs Lisp also provides a more general member-ignore case for contexts where strings matter.

For example working with string values:

ELISP> (member "PRINTED VALUE" '("unprinted value" "printed value")) 
nil
ELISP> (member-ignore-case "PRINTED VALUE" '("unprinted value" "printed value")) 
("printed value")

Or based on strict identity with memq:

ELSIP> (let ((a 'b))
          (memq a '(c b d)))
(b d)

But there is not a function for working with numerical values of different types:

ELISP> (member '2.0 '(1 2 3))
nil

To address this last class of cases means going even further, and by 'further' I mean looking outside of Emacs Lisp. ANSI Common Lisp implements it's member function to take an arbitrary test for equality and this is the sort of thing that might be useful.

And since we're already writing a function...how hard could it be to add that flexibility?

An Implementation of value-in-list

Like most questions that start with "How hard could it be" it turns out that it is not as easy as was initially thought. In this case because there appears to be a 'bug' like behavior as documented in the comments.

(defun value-in-list (value list &optional test)
"Uses test to determine if value is in list.
 If value is in in list, returns the cdr of the list
 which begins with value: the return value mimics the behavior of MEMBER.
 Example: (my-member 3.0 '(1 2 3) '=) -> (3)
          (my-member '(1) '((1) (2) (3)) 'eq) -> nil
 Remarks: This function could in theory be structured as the local function
 'my-member, but Emacs-Lisp runs out of stack space if 'eq or 'eql are passed
 to 'my-member within another function. Seems like a bug."
(let
    ((my-member (lambda (value list test)
                  (if (funcall test value (car list))
                      list
                    (my-member value (cdr list) test)))))
  (cond
   ((null test)
    (member value list))
   ((eq test 'eq)
    (memq value list))
   ((eq test 'eql)
    (memql value list))
   (t (funcall my-member value list test)))))

Highlights

Note the use funcall in two places where a symbol bound to a function is passed as a parameter to another function:

  1. Within the locally defined function my-member.
  2. In the call to my-member in the t condition of the cond expression.

This is necessary within Emacs Lisp and one of the ways it varies from Common Lisp and various Schemes.

Example Use Despite the difficulties that add complexity, value-in-list provides a great deal of flexibility:

ELISP> (value-in-list 2 '(1 2 3))
(2 3)

ELISP> (value-in-list '(2) '(1 (2) 3))
((2)
 3)

ELISP> (value-in-list 1 '(1 2 3) #'(lambda (x y) (< x y)))
(2 3)

ELISP> (value-in-list 2.0 '(1 2 3) '=)
(2 3)

ELISP> (value-in-list 2
                     '(1 (2) 3)
                     #'(lambda (x y) (if (consp y)
                                     (= x (car y)))))
((2)
 3)

Final Thoughts

Shorter is so long as it does not come at the cost of power. Writing one's own version of a function provides an opportunity to make a pneumatic tire along with the risk of reinventing the wheel.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.