Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

lisp filter out results from list not matching predicate

I am trying to learn lisp, using emacs dialect and I have a question. let us say list has some members, for which predicate evaluates to false. how do I create a new list without those members? something like { A in L: p(A) is true }. in python there is filter function, is there something equivalent in lisp? if not, how do I do it?

Thanks

like image 749
Anycorn Avatar asked Feb 10 '10 06:02

Anycorn


5 Answers

These functions are in the CL package, you will need to (require 'cl) to use them:

(remove-if-not #'evenp '(1 2 3 4 5))

This will return a new list with all even numbers from the argument.

Also look up delete-if-not, which does the same, but modifies its argument list.

like image 66
rootzlevel Avatar answered Nov 02 '22 06:11

rootzlevel


If you manipulate lists heavily in your code, please use dash.el modern functional programming library, instead of writing boilerplate code and reinventing the wheel. It has every function to work with lists, trees, function application and flow control you can ever imagine. To keep all elements that match a predicate and remove others you need -filter:

(-filter (lambda (x) (> x 2)) '(1 2 3 4 5)) ; (3 4 5)

Other functions of interest include -remove, -take-while, -drop-while:

(-remove (lambda (x) (> x 2)) '(1 2 3 4 5)) ; (1 2)    
(-take-while (lambda (x) (< x 3)) '(1 2 3 2 1)) ; (1 2)
(-drop-while (lambda (x) (< x 3)) '(1 2 3 2 1)) ; (3 2 1)

What is great about dash.el is that it supports anaphoric macros. Anaphoric macros behave like functions, but they allow special syntax to make code more concise. Instead of providing an anonymous function as an argument, just write an s-expression and use it instead of a local variable, like x in the previous examples. Corresponding anaphoric macros start with 2 dashes instead of one:

(--filter (> it 2) '(1 2 3 4 5)) ; (3 4 5)
(--remove (> it 2) '(1 2 3 4 5)) ; (1 2)
(--take-while (< it 3) '(1 2 3 2 1)) ; (1 2)
(--drop-while (< it 3) '(1 2 3 2 1)) ; (3 2 1)
like image 38
Mirzhan Irkegulov Avatar answered Nov 02 '22 06:11

Mirzhan Irkegulov


I was looking for the very same last night and came across the Elisp Cookbook on EmacsWiki. The section on Lists/Sequences contains filtering teqniques and show how this can be done with mapcar and delq. I had to mod the code to use it for my own purposes but here is the original:

;; Emacs Lisp doesn’t come with a ‘filter’ function to keep elements that satisfy 
;; a conditional and excise the elements that do not satisfy it. One can use ‘mapcar’ 
;; to iterate over a list with a conditional, and then use ‘delq’ to remove the ‘nil’  
;; values.

   (defun my-filter (condp lst)
     (delq nil
           (mapcar (lambda (x) (and (funcall condp x) x)) lst)))

;; Therefore

  (my-filter 'identity my-list)

;; is equivalent to

  (delq nil my-list)

;; For example:

  (let ((num-list '(1 'a 2 "nil" 3 nil 4)))
    (my-filter 'numberp num-list))   ==> (1 2 3 4)

;; Actually the package cl-seq contains the functions remove-if and remove-if-not. 
;; The latter can be used instead of my-filter.
like image 21
kjfletch Avatar answered Nov 02 '22 07:11

kjfletch


Emacs now comes with the library seq.el, use seq-remove.

seq-remove (pred sequence) 
"Return a list of all the elements for which (PRED element) is nil in SEQUENCE."
like image 7
Joelmob Avatar answered Nov 02 '22 07:11

Joelmob


With common lisp, you can implement the function as follows:

(defun my-filter  (f args)
    (cond ((null args) nil)
        ((if (funcall f (car args))
            (cons (car args) (my-filter  f (cdr args)))
            (my-filter  f (cdr args))))))

(print 
      (my-filter #'evenp '(1 2 3 4 5)))
like image 1
prosseek Avatar answered Nov 02 '22 05:11

prosseek