java.lang.Long cannot be cast to clojure.lang.PersistentCollection

Hey folks,

Clojure beginner trying to debug a function I wrote for a homework assignment (see below). We’re supposed to do this recursively and I’m trying to nail it down but running into the error that I made the title of this post. From what I found online, it’s making reference to a number being used where a collection should be and the compiler doesn’t like it. Not sure where the problem lies. Any tips or tweaks to the function I can make? My function is here. The compiler makes reference to line 39.

Create a function of 3 arguments, (merge-pred pred lst1 lst2), where pred is a predicate function
of two arguments, and lst1 and lst2 are lists. pred will indicate an ordering relationship. (pred a b)
returns true if and only if a ≤p b and returns false otherwise. Note that this is the reverse of the ordering predicate used in max-pred. The arguments lst1 and lst2 must already be sorted in increasing order according to pred. In the event of a tie (pred considers two elements to be “equal”), the elements from lst1 should appear before the tied elements of lst2. Note: “equal” does not mean the same. For example, if pred compares elements based on their string length, ”hello” and ”olleh” would be considered equal. The return value of merge-pred should be the results of merging the elements of the lists in sorted order (according to pred). Sample runs:

(merge-pred <= ’(1 4) ’(1 2 8))
(1 1 2 4 8)
(merge-pred #(<= (count %1) (count %2)) ’(“a” “ab” “abcdef”) ’(“c” “abc”))
(“a” “c” “ab” “abc” “abcdef”)

conj expects a collection as the first argument and one or more elements to add to the collection as the second and subsequent arguments.

That works on line 37 because you have (conj collection (first collection)) but on line 39 you have (conj (first collection) collection) so that first argument is a Long, not a collection.

You can use cons there instead which expects (cons element collection) and will produce a new collection that starts with that element, followed by all the items in collection.

(I won’t make any other comments on the code until you have it working and are looking for feedback on improving it)

Ahh, was not aware cons was an option. Thanks for that!

What suggestions would you make for improvements?

(merge-pred <= ’(1 4) ’(1 2 8)) returns (1 4 1 2 8) and (merge-pred >= ’(1 4) ’(1 2 8)) returns (4 4 8 2 1) so it’s getting closer to the correct output.

Although you’re taking the first element of the sorted collection when merging results, you then merge the unsorted (rest collection) – so the element you’re dropping is the first element before sorting.

Are you referring to lines 37 and 39? I tried sorting both of those (rest lst) calls but the outputs were the same.

(conj (merge-pred pred (sort pred (rest lst1)) lst2) (first (sort pred lst1)))

I wonder if things would be simpler if you just assumed the inputs were sorted, and handled that requirement elsewhere (in another function that passes sorted lists to this one).

Consider lst1 being (5 1 2 3 4)(rest lst1) is (1 2 3 4) so it’s sorted already.

But if you sort the whole list you get (1 2 3 4 5) and the first of that is 1 – so you lose 5 and you have 1 in both places.

That part makes sense. What I don’t get is the output was the same even before I sorted that list. I guess I’m misunderstanding what you meant by the below:

Although you’re taking the first element of the sorted collection when merging results, you then merge the unsorted (rest collection) – so the element you’re dropping is the first element before sorting.

Were you referring to (rest lst1)? That’s what I thought you were referring to so that’s whaat I was attempting to sort. The output was the same.

I second that. Note that if we’re allowed to use a (stable) sorting function, then there’s little to be won by not taking, say, (defn merge-pred [p xs ys] (sort p (concat xs ys)))¹.

(From how the assignment is stated – which also says the inputs “must already be sorted” – I’d expect relying on sorting oracles to be prohibited, since such merge functions are then often used to implement sorting in the first place.)

¹ P.S. I see now that clojure’s sort is stable when given strict order relation, so we’d have to go with

(defn merge-pred [p xs ys] (sort p (concat ys xs)))

(merge-pred #(<= (count %1) (count %2)) ["" "a" "b" "c" "def"] ["d" "e" "foo" "bar"])
;; => ("" "c" "b" "a" "e" "d" "def" "bar" "foo")

or

(defn merge-pred [p xs ys] (sort #(not (p %2 %1)) (concat xs ys)))

(merge-pred #(<= (count %1) (count %2)) ["" "a" "b" "c" "def"] ["d" "e" "foo" "bar"])
;; => ("" "a" "b" "c" "d" "e" "def" "foo" "bar")

Both satisfy OP’s requirements afaics, although first one probably not guaranteed to do so. Still, I’d assume that using sort in any way is not in the spirit of the assignment, even though it’s not explicitly stated.

@yap0001 In case this represents your current solution, note that it does not yet quite satisfy

In the event of a tie (pred considers two elements to be “equal”), the elements from lst1 should appear before the tied elements of lst2

, e.g.:

(merge-pred #(<= (count %1) (count %2)) ["a" "b" "cd"] ["" "c" "d" "ef"])
;; => ("" "c" "d" "a" "b" "cd" "ef")

(here “a” and “b” should come before “c” and “d”).

Hth

Figured this out! Thanks all!