I'm new in clojure and try to write simple function which get list of numbers and filter only even numbers.
I want to do it witout filter or even?, only pure clojure
(defn my-even [ilist]
(if
(= (mod (first ilist) 2) 0)
(concat (list (first ilist)) (my-even (rest ilist)))
(my-even (rest ilist))
)
)
开发者_Python百科
I try to run it:
(my-even '(1,2,3,4,5))
But get error:
#<CompilerException java.lang.NullPointerException (NO_SOURCE_FILE:0)>
What's wrong?
Thank you.
As Jonas said, you do not have a base case; in addition to that it is not idiomatic Clojure (or any other Lisp) to put parens on separate lines, also keep the if
's predicate on the same line.
With destructuring it is a bit more readable:
(defn my-even? [coll]
(if-let [[first & rest] coll]
(if (= (mod first 2) 0)
(cons first (my-even? rest))
(my-even? rest))))
Your recursive function my-even
does not have a base case. What happens when there are no more elements in the list? (first ilist)
returns nil
and (mod nil 2)
throws a NullPointerException.
You must somehow test for the empty list.
It's great to see so many people learning Clojure this week :) starting with a fundamental problem like this is a really good start. Hamza and Jonas's answers clearly cover the original question quite well. I would like to offer some unsolicited advice on where to take it from here in the hopes that it will be helpful.
Once you have the base recursive form you can turn it into idiomatic Clojure generally by:
1) use tail recursive forms when you can (you already did this)
2) replace direct recursion with the recur
call to keep from blowing the stack. (starting with Hamza's working answer)
(defn my-even? [coll]
(if-let [[first & rest] coll]
(if (= (mod first 2) 0)
(cons first (my-even? rest))
(recur rest))))
the recur
causes the compiler to jump to the start of the stack frame instead of allocating a new one. without this it will blow the stack.
3) in many cases you can eliminate the (defn [] ... (recur))
pattern with a higher order function like map
, reduce
, filter
, for
, etc. In this excercise I see you are trying to not use filter
or even
, so obviously you could write my-filter and my-even and that would be ok ;)
4) extract the divisible parts, (building a list, choosing what to include) into reusable functions and upload any that are generally useful to a clojure contrib project :)
5) think very carefully should you find yourself using (lazy-seq ...)
as there is a good chance you are re-inventing the wheel.
Here is another solution that does not require destructuring, only basic lisp and scheme-like functions.
(defn my-even [ilist]
(cond (empty? ilist) '() ;; base case
(= (mod (first ilist) 2) 0)
(cons (first ilist) (my-even (next ilist)))
:else (my-even (next ilist))))
精彩评论