Sum until limit with Clojure

clojure

#1

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?


#2

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.


#3

Note first that

(range 1 6)

returns
(1 2 3 4 5)

and

(apply + (range 1 6))

returns

15


#4

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.


#5

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.


#6

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.


#7

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


#8

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