2

I have a list of functions, a list of elements, and I'd like to apply all the functions on all the elements then append all the resulting lists together. I did it as follow

(defun apply-functions(funcs elements)
    (if (null funcs)
        nil
        (append (mapcar #'(lambda (x) (funcall (car funcs) x)) elements) (apply-functions (rest funcs) elements))))

It works as intended, but I don't like it. Is there a cleaner, more concise way of doing it?. I am new to lisp, and still getting used to the lispish style of doing things.

3 Answers 3

6

I don't know if you like loop macro (and I don't want to spoil anyone), but try this:

(defun apply-functions (fs es)
    (loop for f in fs appending (mapcar f es)))
Sign up to request clarification or add additional context in comments.

1 Comment

Haven't used loop before, but it looks great. I'll read on that. Thanks a bunch.
6

This is the same idea as yours, just shorter:

(defun apply-functions (functions elements)
  (mapcan #'(lambda (x) (mapcar x elements)) functions))

3 Comments

This is nice, (car x) is not necessary here though, right?..shouldn't it just be mapcar x elements ?
how about using mapcan instead of mapcon? That should get rid of the (car x), and instead just have x.
This version is very nice. Is this more efficient than the looping version?
4

I would define a function, call-each that returns a new function, returning the list of calling each function on it's argument:

(defun call-each (fns)
  (lambda (arg)
    (mapcar (lambda (fn)
              (funcall fn arg))
            fns)))

(funcall (call-each (list #'third #'second #'first)) '(a b c))
;=> (C B A)

cl has the function mapcan which is basically nconc + mapcar :

(mapcan #'reverse '((a b c)
                    (e f g)
                    (h i j)))
;=> (C B A G F E J I H)

(mapcan (call-each (list #'identity #'1+)) '(1 3 5 7 9))
;=> (1 2 3 4 5 6 7 8 9 10)  

unfortunately, nconc, which mapcan uses, is destructive:

(let ((data '((a b c)
              (d e f)
              (g h i))))
  ;;here be dragons
  (list (mapcan #'identity data)
        data))
;=> ((A B C D E F G H I) ((A B C D E F G H I) (D E F G H I) (G H I)))

alexandria to the rescue:

(let ((data '((a b c)
              (d e f)
              (g h i))))
  ;;safe version
  (list (alexandria:mappend #'identity data)
        data))
;=> ((A B C D E F G H I) ((A B C) (D E F) (G H I)))

note that using mapcan is more efficient, but unless you know exactly where your data is coming from, and who owns it, mappend is the way to go.

so you could write:

(defun apply-functions (fs es)
  (when fs
    (alexandria:mappend (call-each fs) es))

(apply-functions (list #'identity #'1+) '(1 3 5 7 9))
;=> (1 2 3 4 5 6 7 8 9 10)

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.