Search code examples
clojuresicp

Let binding sequence as input to map, exception thrown


Coming from Functional Java and a little Scala, I am now learning Clojure step by step. At the moment, given that I have free time, I am reading and doing exercises from "Structure and Interpretation of Computer Programs".

I am stuck at the following exercise, where I know how to solve the problem, which is by completely removing let and put (take 2 ... directly in map.

; Exercise 1.3
;; Define a procedure that takes three numbers as arguments 
;; and returns the sum of the squares of the two larger numbers.
(defn square-sum-largest-pair [a b c]
  "Sums the square of the largest two number of the three in input.
  Maybe a little overkill to use a list here, but it's just for fun."
  (let [[two-items] (take 2 (reverse (sort [a b c])))]
  (reduce + (map (fn [x] (m/expt x 2)) two-items))))

The following error is displayed when I execute square-sum-largest-pair:

actual: java.lang.IllegalArgumentException: Don't know how to create ISeq from:     java.lang.Long
at clojure.lang.RT.seqFrom (RT.java:505)
    clojure.lang.RT.seq (RT.java:486)
    clojure.core$seq.invoke (core.clj:133)
    clojure.core$map$fn__4245.invoke (core.clj:2551)

Why is this happening?


Solution

  • The problem is the [] round two-items. This binds two-items to the first of the sequence (take 2 (reverse (sort [a b c]))), ignoring the rest. Trying to use this - the maximum number - as a sequence in the subsequent map produces the observed error.

    Removing the [] (and squaring with * for simplicity), we get

    (defn square-sum-largest-pair [a b c]
      (let [two-items (take 2 (reverse (sort [a b c])))]
        (reduce + (map (fn [x] (* x x)) two-items))))
    
    (square-sum-largest-pair 1 2 3)
    13