Hi everybody I've been recently trying to learn to a new language and I sort of bumped into Clojure which look like a real interesting language because I've never heard about functional programming, even though I had used JavaScript before that kind of leverages it, well I'm gonna stop with the small talk and get into the problem.
I've been working on solving the https://github.com/gigasquid/wonderland-clojure-katas and more specific on the doublets problem. I think I have came with a solution but it send me the error on the title of this post. I have read about this error and it seems that it triggers when you want the compiler expects a function but it doesn't. Here is the full code of my solution to see if you can help me out with this one:
(ns doublets.solver
(:require [clojure.java.io :as io]
[clojure.edn :as edn]
[clojure.set :as set]))
(def words (-> "words.edn"
(io/resource)
(slurp)
(read-string)))
(defn linked-word [word word-list]
(some #(when (= (count (set/difference (into #{} (seq %))
(into #{} (seq word)))) 1) %)
word-list))
(defn doublets [word1 word2]
(let [n (count word1) v (cons word1 (filter #(= (count %) n)
(remove #{word1} words)))]
(tree-seq #(and (linked-word (% 0) %) (not= (% 0) word2))
#(cons (linked-word (% 0) (rest %))
(remove #{(% 0)} (rest %))) v)))
As you can see cons is a function so the error doesn't seem to be the case described above.
I can reproduce the error after downloading the words.edn
file and running with (doublets "bank" "loan")
. I think the problem is these expressions:
(% 0)
which you have in a few places. I see that you are cons-ing
some things, so that may be a clue. What is (% 0)
supposed to do?
If you want the first char, just say (first xyz)
or something.
I would also break out the anonymous functions #(...)
and give them real names.
My guess seems to be correct as this experiment shows:
(cons 1 [2 3]) => (1 2 3)
(class (cons 1 [2 3])) => clojure.lang.Cons
(vec (cons 1 [2 3])) => [1 2 3]
(class (vec (cons 1 [2 3]))) => clojure.lang.PersistentVector
OK, rewrite like:
(defn doublets [word1 word2]
(let [n (count word1)
v (vec (cons word1 (filter #(= (count %) n)
(remove #{word1} words))))]
(tree-seq
#(and
(linked-word (% 0) %)
(not= (% 0) word2))
#(vec (cons (linked-word (% 0) (rest %)))
(remove #{(% 0)} (rest %)))
v)))
new error: java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol
And there is the clue we needed!
We are evaluating things as symbols, not strings! The problem is read-string
, which is how you read source code, not data like strings. Delete read-string
:
(def words (-> "words.edn"
(io/resource)
(slurp)))
We now get a new error on this line:
v (vec (cons word1 (filter #(= (count %) n)
(remove #{word1} words))))]
ERROR in (dotest-line-40) (RT.java:664)
Uncaught exception, not in assertion.
expected: nil
actual: java.lang.UnsupportedOperationException:
count not supported on this type: Character
So your seq
has created something like "foo" => [\f \o \o], and you then try to say (count \f)
. You can't count a single char, only a string.
I'll let you debug it from there.