0

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?

Andrea Richiardi
  • 703
  • 6
  • 21

3 Answers3

3

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
Thumbnail
  • 13,293
  • 2
  • 29
  • 37
  • I will flag this one as correct as it is identifying and clearly solving the problem...but the other one is a nice and elegant solution as well. Have a look (and if you want fork) at my repo at https://github.com/arichiardi/sicp-clojure. It is great fun for me and I would like to have some opinion from Clojure experts. – Andrea Richiardi Aug 07 '14 at 16:26
3

Because you are always dealing with the same fixed number of items from the front of the seq, you can also tackle this by using destructuring to create bindings for the first elements:

(defn square-sum-largest-pair [a b c]
  (let [[x y] (reverse (sort [a b c]))]
    (+ (* x x) (* y y))))
gregspurrier
  • 1,448
  • 2
  • 13
  • 13
1

Don't put brackets around two-times. As a bonus, I modified it so you don't have to reverse the sorted list:

(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 (sort > [a b c]))]
    (reduce + (map (fn [x] (m/expt x 2)) two-items))))
YosemiteMark
  • 675
  • 1
  • 6
  • 13