Sum until limit with Clojure

The way Clojure code solves things are very elegant, but unusual for people without a solid background in fully functional programming languages (like me).

I’ve tried to solve simple problems from Project Euler using Clojure, but failed due the lack of that background.

One problem I was trying to solve was to sum the numbers until a limit. Let’s say I want the sum until 5, so, the math would be 1 + 2 + 3 + 4 + 5 = 15.

In JavaScript I would do something like:

function sumUntilLimit(limit) {
  let counter = 0,
  result = 0;
  while(counter <= limit) {
    result += counter;
    counter++;
  }
  return result;
}

sumUntilLimit(5); // 15

How would you do accomplish the right result using Clojure?

I’m pretty new to all of this but I would write that function in clojure like:

(defn sum-until-limit [limit] (apply + (range (inc limit))))

Not sure how to do formatting on here so I wouldn’t have that as one line. But basically apply is going to take a given function (in this case, +) and apply it as the operand for all the items in a given collection. So (apply + [1 2 3]) will give you (+ 1 2 3). So in this case we take the range from 0 to (inc limit) because we want to add 1 to the limit given because range is non inclusive. (range 6) gives us the sequence: (0 1 2 3 4 5) I hope I’m explaining all that correctly. Seems to work in my repl at least.

2 Likes

Note first that

(range 1 6)

returns
(1 2 3 4 5)

and

(apply + (range 1 6))

returns

15

2 Likes

I like that suggestion. That also allows you to use this for multiplication too as it takes the 0 out of the equation. You still need to increment your “limit” so it would have to be (apply + (range 1 (inc limit)). My first suggestion might be more elegant now but still isn’t as generic.

Great approaches guys. I knew about apply for it’s very common in JavaScript, but didn’t know that there was a range function as in Python.

Thank you.

A few other ways to explore:

Using reduce instead of apply:

(defn sum-until
  [limit]
  (reduce + (range 1 (inc limit))))

Recursively, without using range:

(defn sum-until
  [limit]
  (loop [limit limit sum 0]
    (if (pos? limit)
      (recur (dec limit)
      (+ sum limit)) sum)))

Iteratively, without using range:

(defn sum-until
  [limit]
  (let [result (atom 0)
        limit (atom limit)]
    (while (pos? @limit)
      (swap! result + @limit)
      (swap! limit dec))
    @result))

Using iterate (a type of immutable generator), without range

(defn sum-until [limit]
  (->> (iterate (fn [[result counter]]
                  [(+ result counter)
                   (inc counter)])
                [0 1])
       (take-while #(<= (last %) (inc limit)))
       (map first)))

That last one is cool, because you can actually generate all result at every step:

(sum-until 5)
;; Returns: (0 1 3 6 10 15)

Which you can also implement in a mutable way:

(defn sum-until [limit]
  (let [counter (atom 0)]
    (->> (iterate #(do (swap! counter inc)
                       (+ @counter %))
                  0)
         (take-while (fn[_] (>= limit @counter))))))

Keeping in mind it is most idiomatic to use range with either reduce or apply.

4 Likes

Okay – that iterate one taught me few things. Thanks! Now I’m reading https://clojuredocs.org/clojure.core/iterate and imagining the possibilities…

You can also obtain the intermediate steps by using reductions instead of reduce in your first implementation.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.