1

I need to make filter method, but i can't return method name as argument, not as result.

In my case, i need to input odd? method as argument and call recursion.

I can use only this construction:

(defn my-filter [p xn])

My code:

(defn my-filter [p xs]
  (if (p (first xs))
      (cons (first xs) (recur p (next xs))) 
      (recur p (next xs) )))


(my-filter odd? '(1 2 3 4 5))

Error: IllegalArgumentException Argument must be an integer: clojure.core/even? (core.clj:1372)

As i can see, where recursion is called, arguments are calculating result, instead of call recursion with odd? and (next xs) arguments

1
  • 1
    (The indentation is misleading - the second recurring "my-filter" is the "else" part of the "if".) Commented Dec 19, 2020 at 22:37

3 Answers 3

3

Two issues need attention. Or maybe only one issue, if you don't need to handle very long lists. 1) The function does not notice when the inputs are exhausted. Open a REPL and try (odd? nil) and you will see what happens! 2) If you try the function on a really long list, you might get a StackOverflow. The clojure.org guide for recursion has an example of how to avoid that problem - actually it illustrates solutions to both problems: https://clojure.org/guides/learn/flow#_recursion

Sign up to request clarification or add additional context in comments.

Comments

0

Your code does not compile:

Syntax error (UnsupportedOperationException) compiling recur at ... .
Can only recur from tail position

You have to replace the recurs with explicit recursive calls to my-filter:

(defn my-filter [p xs]
  (if (p (first xs))
    (cons (first xs) (my-filter p (next xs)))
    (my-filter p (next xs))))

Now it compiles, but ...

(my-filter odd? [])
Execution error (IllegalArgumentException) at ...
Argument must be an integer: 

You need to check that the sequence argument xs is not empty before doing anything else with it:

(defn my-filter [p xs]
  (when (seq xs)
    (if (p (first xs))
      (cons (first xs) (my-filter p (rest xs)))
      (my-filter p (rest xs)))))

The when evaluates to nil if the condition fails. The nil, called on to be a sequence, behaves as an empty one. So ...

(my-filter odd? [])
=> nil
(my-filter odd? (range 10))
=> (1 3 5 7 9)

It works. However, it evaluates (first xs) twice, and mentions (my-filter p (rest xs)) twice. Factoring these out, we get

(defn my-filter [p xs]
  (when (seq xs)
    (let [head (first xs)
          tail (my-filter p (rest xs))]
      (if (p head) (cons head tail) tail))))

This uses direct recursion. So it runs out of stack on a long sequence:

(count (my-filter odd? (range 10000)))
Execution error (StackOverflowError) at ...

Wrapping the recursion in lazy-seq flattens the evaluation, devolving it to whatever explores the sequence:

(defn my-filter [p xs]
  (lazy-seq
    (when (seq xs)
      (let [head (first xs)
            tail (my-filter p (rest xs))]
        (if (p head) (cons head tail) tail)))))

Now ...

(count (my-filter odd? (range 10000)))
=> 5000

If you want an eager version, you had better build the returned sequence as a vector:

(defn eager-filter [p coll]
  (loop [answer [], coll (seq coll)]
    (if-let [[x & xs] coll]
      (recur
        (if (p x) (conj answer x) answer)
        xs)
      (sequence answer))))

This won't run out of stack:

(count (eager-filter odd? (range 10000)))
=> 5000

But it can't handle an endless sequence:

(first (eager-filter odd? (range)))

Process finished with exit code 137 (interrupted by signal 9: SIGKILL)

I had to kill the process.

Comments

0

I really like this solution, therefore I share with you. Very simple. (I know, that is not exactly what was the question but different approach.)

(def data (vec (range 10)))

Map implementation

(defn -map [f coll]
  (reduce
    (fn [acc v]
      (conj acc (f v)))
    []
    coll))

Filter implementation

(defn -filter [f coll]
  (reduce
    (fn [acc v]
      (if (f v)
        (conj acc v))
    []
    coll))

Example usage

(->> data
     (-map inc)
     (-filter odd?))

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.