0

I just wanted to write a simple little guessing game in Clojure, and I got this error. I can't see where is a Character treated like a function in my, as the resulted input also predicts that there should not be such problem. Here's the code:

(ns clojure.examples.hello
  (:gen-class))

(ns clojure-noob.core)

(defn takeFst [x n]
    (if (= n 0) () (cons (first x) (takeFst (rest x) (- n 1))))
)

(defn createSeq [elem n]
    (if (= n 0) () (cons elem (createSeq elem (- n 1))))
)

(defn fnEquals? [n]
    (fn [elem] (= elem n))
)

(defn removeEach [x elem]
    (remove (fnEquals? elem) x)
)

(defn containsString? [s ch]
    (not (empty? (filter true? (map = (createSeq ch (count s)) s))))
)

(defn akasztofa! [s lives]
    (println s)
    (if (and (not= () s) (not= lives 0))
        (
            (def guess (eval (read)))
            (if (containsString? s guess) (akasztofa! (removeEach s guess) lives) (akasztofa! s (- lives 1)))
        )
        ()
    )
)

(akasztofa! "hab" 10)

The output I get is this:

hab
(a b)
(b)
() 
Exception in thread "main" java.lang.ClassCastException: 
java.lang.Character cannot be cast to clojure.lang.IFn, compiling:
(/home/cicaharcos/Documents/Clojure/First/akasztofa/main.clj:38:1)

My input was: \h \a \b

2 Answers 2

2

The error comes from trying to evaluate a character as a function, for example:

(\a) => Exception in thread "main" java.lang.ClassCastException: java.base/java.lang.Character cannot be cast to clojure.lang.IFn, 

I think it is the extra parens in the if statement that are trying to evaluate the character as a function. Remember, in Clojure parentheses are not "grouping" like in Java, they mean "function call".

Your code has some other problems, notable using () for an empty list. You must either quote the list like this:

'()

or, better yet, use an empty vector with square brackets (which requires no quoting):

 []

If you make the code look like the following, it seems to work:

(ns clojure.examples.hello
  (:gen-class))

(ns clojure-noob.core)

(defn takeFst [x n]
    (if (= n 0) [] (cons (first x) (takeFst (rest x) (- n 1))))
)

(defn createSeq [elem n]
    (if (= n 0) [] (cons elem (createSeq elem (- n 1))))
)

(defn fnEquals? [n]
    (fn [elem] (= elem n))
)

(defn removeEach [x elem]
    (remove (fnEquals? elem) x)
)

(defn containsString? [s ch]
    (not (empty? (filter true? (map = (createSeq ch (count s)) s))))
)

(defn akasztofa! [s lives]
    (println s)
    (if (and (not= [] s) (not= lives 0))
        (let [guess (eval (read))]
          (if (containsString? s guess)
            (akasztofa! (removeEach s guess) lives)
            (akasztofa! s (- lives 1))))
        [] ))

(akasztofa! "hab" 10)

Results:

hab
\h      ; <= user input plus <ret>
(a b)
\a      ; <= user input plus <ret>
(b)
\b      ; <= user input plus <ret>
()

Re Empty Lists:

Using the count function, you can see the problem:

demo.core=> (count ())
0

demo.core=> (count (\b))

ClassCastException java.base/java.lang.Character cannot be cast to clojure.lang.IFn  demo.core/eval16682 (form-init2403719904611886388.clj:1)

demo.core=> (count (1))

ClassCastException java.base/java.lang.Long cannot be cast to clojure.lang.IFn  demo.core/eval16686 (form-init2403719904611886388.clj:1)

demo.core=> (count '(\b))
1

So you can use () (unquoted) for an empty list (which I forgot), but it fails if it is non-empty unless you quote it. Using a vector is simpler and less error-prone:

demo.core=> (count [])
0
demo.core=> (count [\b])
1
demo.core=> (count [1])
1
Sign up to request clarification or add additional context in comments.

5 Comments

I know what the error message means, I just don't know where I made the mistake. And as you can see from the output, it still prints out the empty list and then fails, which absolutely doesn't make sense. By the way what's wrong with notating an empty list like this () ?
Note how the empty parens in the original code has been replaced with a (let ...) form in the new version of the code. The empty parens were the cause of the exception.
Well, I replaced all the unquoted empty lists with quoted ones and I tried vectors as well, but I get the same error
Oh yeah, the let, I forgot about that one
'() and () are the same. Yet to see, if a sequence is not empty, (seq s) would do.
2

You are trying to evaluate a character as a function. You should avoid using eval in your code. Instead, work with a list of symbols that come from read function. That is much safer than evaluating user's input.

Do not use def inside a function, only at the top of a module. Def declares a new entity globally among the entire namespace. Use let form that creates a local scope where its variables live until the evaluation exists from it.

Also, your function calls itself recursively. That's alright for short game sessions, but could cause troubles for long ones. In your case, the function akasztofa! fits the tail recursion optimization criteria (TRO) so that you may replace inner calls with recur form:

(recur (removeEach s guess) lives)
(recur s (- lives 1))))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.