0
votes

I am coming from a Java background trying to learn Clojure. As the best way of learning is by actually writing some code, I took a very simple example of finding even numbers in a vector. Below is the piece of code I wrote:

`

(defn even-vector-2 [input]
  (def output [])
  (loop [x input]
    (if (not= (count x) 0)
      (do
        (if (= (mod (first x) 2) 0)
          (do
            (def output (conj output (first x)))))
        (recur (rest x)))))
  output)

`

This code works, but it is lame that I had to use a global symbol to make it work. The reason I had to use the global symbol is because I wanted to change the state of the symbol every time I find an even number in the vector. let doesn't allow me to change the value of the symbol. Is there a way this can be achieved without using global symbols / atoms.

7
Why are you trying to mutate output? Just make it another parameter of the loop. Never use def inside a function. It's never necessary unless you're doing language level stuff. - Carcigenicate
Anyways, this can be solved like (filter #(= (rem % 2) 0)) [1 2 3 4]) (Sorry if I've misplaced a parenthesis. Clojure is hard to write on phones). - Carcigenicate
When I get home, I'll write a answer to elaborate. - Carcigenicate

7 Answers

6
votes

The idiomatic solution is straightfoward:

(filter even? [1 2 3])
; -> (2)

For your educational purposes an implementation with loop/recur

(defn filter-even [v]
  (loop [r []
         [x & xs :as v] v]
    (if (seq v) ;; if current v is not empty
      (if (even? x)
        (recur (conj r x) xs) ;; bind r to r with x, bind v to rest
        (recur r xs)) ;; leave r as is
      r))) ;; terminate by not calling recur, return r
2
votes

The main problem with your code is you're polluting the namespace by using def. You should never really use def inside a function. If you absolutely need mutability, use an atom or similar object.

Now, for your question. If you want to do this the "hard way", just make output a part of the loop:

(defn even-vector-3 [input]
  (loop [[n & rest-input] input ; Deconstruct the head from the tail
         output []] ; Output is just looped with the input
    (if n ; n will be nil if the list is empty
        (recur rest-input
               (if (= (mod n 2) 0)
                 (conj output n)
                 output)) ; Adding nothing since the number is odd
        output)))

Rarely is explicit looping necessary though. This is a typical case for a fold: you want to accumulate a list that's a variable-length version of another list. This is a quick version:

(defn even-vector-4 [input]
  (reduce ; Reducing the input into another list
    (fn [acc n]
      (if (= (rem n 2) 0)
        (conj acc n)
        acc))
    [] ; This is the initial accumulator.
    input))

Really though, you're just filtering a list. Just use the core's filter:

(filter #(= (rem % 2) 0) [1 2 3 4])

Note, filter is lazy.

1
votes

Try

#(filterv even? %)

if you want to return a vector or

#(filter even? %)

if you want a lazy sequence.

If you want to combine this with more transformations, you might want to go for a transducer:

(filter even?)
1
votes

If you wanted to write it using loop/recur, I'd do it like this:

(defn keep-even 
  "Accepts a vector of numbers, returning a vector of the even ones."
  [input]
  (loop [result []
         unused input]
    (if (empty? unused)
      result
      (let [curr-value    (first unused)
            next-result   (if (is-even? curr-value)
                            (conj result curr-value)
                            result)
            next-unused   (rest unused) ]
        (recur next-result next-unused)))))

This gets the same result as the built-in filter function.

0
votes

Take a look at filter, even? and vec check out http://cljs.info/cheatsheet/

(defn even-vector-2 [input](vec(filter even? input)))
0
votes

If you want a lazy solution, filter is your friend.

Here is a non-lazy simple solution (loop/recur can be avoided if you apply always the same function without precise work) :

(defn keep-even-numbers
  [coll]
  (reduce
    (fn [agg nb]
      (if (zero? (rem nb 2)) (conj agg nb) agg))
    [] coll))

If you like mutability for "fun", here is a solution with temporary mutable collection :

(defn mkeep-even-numbers
  [coll]
    (persistent!
      (reduce
        (fn [agg nb]
          (if (zero? (rem nb 2)) (conj! agg nb) agg))
        (transient []) coll)))

...which is slightly faster !

mod would be better than rem if you extend the odd/even definition to negative integers

You can also replace [] by the collection you want, here a vector !

-3
votes

In Clojure, you generally don't need to write a low-level loop with loop/recur. Here is a quick demo.

(ns tst.clj.core
  (:require
    [tupelo.core :as t]  ))
(t/refer-tupelo)

(defn is-even? 
  "Returns true if x is even, otherwise false."
  [x]
  (zero? (mod x 2)))

; quick sanity checks
(spyx (is-even? 2))
(spyx (is-even? 3))

(defn keep-even 
  "Accepts a vector of numbers, returning a vector of the even ones."
  [input]
  (into []    ; forces result into vector, eagerly
    (filter is-even? input)))

; demonstrate on [0 1 2...9]
(spyx (keep-even (range 10)))

with result:

(is-even? 2) => true
(is-even? 3) => false
(keep-even (range 10)) => [0 2 4 6 8]

Your project.clj needs the following for spyx to work:

 :dependencies [
    [tupelo "0.9.11"]