2

I wrote this macro that rewrites e.g. (sum-expr (1 2 3)) as (+ 1 2 3):

(defmacro sum-expr (expr-list)
  `(+ ,@expr-list))

=> SUM-EXPR

For example:

(sum-expr ((+ 1 3) (* 3 4) (- 8 4)))

=> 20

How can I define an equivalent function using defun?

coredump
  • 37,664
  • 5
  • 43
  • 77
Squalety
  • 21
  • 1
  • 1
    Do you want the function to take as inputs *expressions* or a list of numbers to add? – coredump May 03 '21 at 14:33
  • 4
    `(reduce #'+ ...)` is what you want. There is no purpose in having this be a macro. –  May 03 '21 at 15:37
  • this needs a bit more info for a complete answer - do you need to pass it as a quoted list? Since you can't pass `((+ 1 3) (* 3 4) (- 8 4))` as an argument to a function. You need to pass either `'((+ 1 3) (* 3 4) (- 8 4))` or `(list (+ 1 3) (* 3 4) (- 8 4))`. The first one needs a more complex solution; the latter is simpler – Leonardo Dagnino May 03 '21 at 20:47

2 Answers2

2

As @LeonardoDagnino already mentioned it in comment:

(defun sum-expr (lst)
  (apply #'+ lst))

Would be very lispy, but implementation-dependent CALL-ARGUMENTS-LIMIT limits in some implementations the length of lst to 50 as discussed e.g. here .Therefore, the solution with reduce is cleaner.

Gwang-Jin Kim
  • 9,303
  • 17
  • 30
0

Sounds like you need to evaluate the expressions in your list, and then reduce the resulting list by the addition function.

We can evaluate lisp expressions with eval, which we can apply to each element of the input list with mapcar.

We can then use reduce on the resulting list to find the sum.

(defun sum-expr (list)
  (reduce #'+ (mapcar #'eval list)))

This makes a lot of assumptions about the structure and type of your input, but for a simple problem with well-understood inputs, it should be fine.

(You may be interested in Why exactly is eval evil?)

Michael Cornelius
  • 1,516
  • 1
  • 11
  • 11
  • 1
    Could also use `apply` instead of `reduce` since `+` already accepts multiple arguments. – Leonardo Dagnino May 03 '21 at 20:49
  • You don't need `eval` if you just rely on normal evaluation order, and `reduce` is what you use for arbitrary lists. `Apply` is for argument management, not for shoehorning data into rest arguments. – Svante May 06 '21 at 13:03
  • @Svante If the input specification is more flexible than "a list of expressions", I agree. If, in fact, the requirements are a function that takes a list, I don't see another way. What am I missing? – Michael Cornelius May 06 '21 at 15:37