89

I'm learning Clojure and I'm trying to define a function that take a variable number of parameters (a variadic function) and sum them up (yep, just like the + procedure). However, I don´t know how to implement such function

Everything I can do is:

(defn sum [n1, n2] (+ n1 n2))

Of course this function takes two parameteres and two parameters only. Please teach me how to make it accept (and process) an undefined number of parameters.

rodrigoalvesvieira
  • 7,895
  • 15
  • 63
  • 84

5 Answers5

116

In general, non-commutative case you can use apply:

(defn sum [& args] (apply + args))

Since addition is commutative, something like this should work too:

(defn sum [& args] (reduce + args))

& causes args to be bound to the remainder of the argument list (in this case the whole list, as there's nothing to the left of &).

Obviously defining sum like that doesn't make sense, since instead of:

(sum a b c d e ...)

you can just write:

(+ a b c d e ....)
soulcheck
  • 36,297
  • 6
  • 91
  • 90
  • 3
    Yes, doesn´t make sence, but it is a good illustration to your answer. Thanks. – rodrigoalvesvieira Feb 12 '12 at 04:50
  • 1
    @soulcheck: is there a way to pass a `seq` to your sum function. For example : (sum '(1 2 3)) and the result is 6 ? – avichalp Jun 21 '16 at 17:38
  • 1
    @avichalp that would be another function. just remove `&` from either version – soulcheck Jun 21 '16 at 18:50
  • 1
    @soulcheck: No. I mean using the same function signature. I am a newbie in clojure hence I am able to put my point clearly here. What I would like to know is in python I can use *args and if a function is defined such that it takes *args (eg `def fn(*args): pass`) I can call it by giving a list like fn(*list_of_args). Can I do the same thing in clojure ? – avichalp Jun 21 '16 at 18:55
  • @avichalp the call to `def fn(*args): pass` wouldn't be `fn([1,2,3])` but `fn(1,2,3)` (obviously depending what you want to do exactly, but I'm trying to follow the spirit here) – soulcheck Jun 21 '16 at 20:26
  • @soulcheck: In python I can pass a list with an star prepended to it like `fn(*[1, 2, 3])` it will be equivalent to `fn(1, 2, 3)`. Is this possible in clojure ? – avichalp Jun 22 '16 at 07:11
  • @avichalp the & creates a seq out of the arguments passed to the function. If you were to pass a seq, you would be passing a seq of seqs and apply would not be able to evaluate the seq because it expects a clojure.lang.Number type based on the arguments asked for by + – fingaz Dec 14 '16 at 06:29
  • 1
    What @avichalp is asking about is Python's star operator, which [unpacks a list argument](https://treyhunner.com/2018/10/asterisks-in-python-what-they-are-and-how-to-use-them/#Asterisks_for_unpacking_into_function_call) into individual arguments in a function call. The answer is no, Clojure doesn't have that, you would use `apply` instead. See https://stackoverflow.com/a/10802391/165673. – Yarin May 15 '21 at 16:01
34

Yehoanathan mentions arity overloading but does not provide a direct example. Here's what he's talking about:

(defn special-sum
  ([] (+ 10 10))
  ([x] (+ 10 x))
  ([x y] (+ x y)))

(special-sum) => 20

(special-sum 50) => 60

(special-sum 50 25) => 75

Devin Walters
  • 587
  • 4
  • 5
17
 (defn my-sum
  ([]  0)                         ; no parameter
  ([x] x)                         ; one parameter
  ([x y] (+ x y))                 ; two parameters
  ([x y & more]                   ; more than two parameters


    (reduce + (my-sum x y) more))
  )
hariszaman
  • 8,202
  • 2
  • 40
  • 59
11

defn is a macro that makes defining functions a little simpler. Clojure supports arity overloading in a single function object, self-reference, and variable-arity functions using &

From http://clojure.org/functional_programming

Peter Coulton
  • 54,789
  • 12
  • 54
  • 72
viebel
  • 19,372
  • 10
  • 49
  • 83
4
(defn sum [& args]
  (print "sum of" args ":" (apply + args)))

This takes any number of arguments and add them up.

aristotll
  • 8,694
  • 6
  • 33
  • 53