How to rewrite this function to be tail-recursive? - clojure

I'm reading the book The Little Schemer, I implement the functions in Clojure, How to rewrite this function as a tail-recursive function in Clojure?
(defn rember*
[a lat]
(cond
(empty? lat) []
(clojure.inspector/atom? (first lat))
(if (= a (first lat))
(rember* a (rest lat))
(cons (first lat)
(rember* a (rest lat))))
:else
(cons (rember* a (first lat))
(rember* a (rest lat)))))

Here is a translation of your code to a version that uses a loop and two lists instead of recursion in order to maintain program state:
(defn rember* [a lat]
(loop [[[op-type a lat] & rest-ops] (list [:rember a lat])
data '()]
(case op-type
nil (first data)
:value (recur rest-ops (cons a data))
:rember (cond
(empty? lat) (recur rest-ops (cons '() data))
(clojure.inspector/atom? (first lat))
(if (= a (first lat))
(recur (cons [:rember a (rest lat)] rest-ops) data)
(recur (into rest-ops [[:cons]
[:rember a (rest lat)]
[:value (first lat)]])
data))
:else (recur (into rest-ops [[:cons]
[:rember a (rest lat)]
[:rember a (first lat)]])
data))
:cons (let [[a b & data] data]
(recur rest-ops (cons (cons b a) data))))))

I agree, loop-recur is the more clojuresk answer!
But for a tail-recursive version, you could also do:
(def atom? (complement coll?))
(defn rember*
([a lat] (rember* a lat []))
([a lat acc]
(cond (empty? lat) (seq acc) ;; or just `acc` when result should be vector
(atom? (first lat)) (if (= a (first lat))
(rember* a (rest lat) acc)
(rember* a (rest lat) (conj acc (first lat))))
:else (rember* a (rest lat) (conj acc (rember* a (first lat)))))))
With this, you can easily build loop-recur by replacing the recursive function call using recur. However note that the last call of rember* cannot be placed by recur - because you cannot nest recur calls.
(defn rember* [a lat]
(loop [a a
lat lat
acc []]
(cond (empty? lat) (seq acc)
(atom? (first lat)) (if (= a (first lat))
(recur a (rest lat) acc)
(recur a (rest lat) (conj acc (first lat))))
:else (recur a (rest lat) (conj acc (rember* a (first lat)))))))

Related

How to delete an element from a nested list?

I have a deeply nested list and I want to delete a given element from all its occurrences in the list. I have this code:
(defn eliminate [value lst]
(defn sub-eliminate [lst]
(def currentItem (first lst))
(if-not (empty? lst)
(if (seq? currentItem)
(cons (sub-eliminate currentItem) (sub-eliminate (rest lst)))
(if (= value currentItem)
(sub-eliminate (rest lst))
(cons currentItem (sub-eliminate (rest lst)))
)
)
'()
)
)
(sub-eliminate lst)
)
But, it doesn't delete at inner levels. Why??
My guess is that you're using vectors as sequences.
(eliminate 3 [3 3])
;()
(eliminate 3 [3 [3]])
;([3])
This would have been trivial to find had you shown us an example: tut, tut!
What's going on?
Although vectors are seqable, they are not sequences:
(seq? [])
;false
At the outer level, you treat lst as a sequence, so first and rest work, since they wrap their argument in an implicit seq. But seq? will fail on any immediately enclosed vector, and those further in won't even be seen.
If you replace seq? with sequential?, lists and vectors will work.
(sequential? [])
;true
More serious, as #noisesmith noted, is your use of def and defn at inner scope. Replace them with let or letfn.
You could also improve your style:
Replace (if-not (empty? lst) ... ) with (if (seq lst) ...).
Use cond to flatten your nested ifs. This requires inverting
the test in (1), so removes the need for it.
Use recur for the tail-recursive case where you find value, as
#Mark does.
If you don't want to see the result, look away now:
(defn eliminate [value lst]
(letfn [(sub-eliminate [lst]
(let [current-item (first lst)]
(cond
(empty? lst) '()
(sequential? current-item) (cons (sub-eliminate current-item)
(sub-eliminate (rest lst)))
(= value current-item) (recur (rest lst))
:else (cons current-item (sub-eliminate (rest lst))))))]
(sub-eliminate lst)))
There is a remaining tender spot:
You invoke (first lst) before you know that lst is not empty. No
harm done: you'll just get nil, which you ignore.
An Alternative Apporach using Destructuring
You can often use destructuring to abbreviate recursive processing of sequences. I'd be inclined to express your function thus:
(defn eliminate [x xs]
((fn rem-x [xs]
(if-let [[y & ys] (seq xs)]
(if (= x y)
(recur ys)
(cons
(if (sequential? y) (rem-x y) y)
(rem-x ys)))
()))
xs))
For the sake of learning take a look at this function:
(define rember*
(lambda (x l)
(cond ((null? l) '())
((atom? (car l))
(if (eq? (car l) x)
(rember* x (cdr l))
(cons (car l)
(rember* x (cdr l)))))
(else (cons (rember* x (car l))
(rember* x (cdr l)))))))
This is a simple recursive function from book 'The Little Schemer', which is a good source to learn how to write such recursive functions.
Let's see if we can translate it into Clojure:
(defn rember* [x l]
(cond (empty? l) '()
(seq? (first l)) (cons (rember* x (first l))
(rember* x (rest l)))
:else (if (= (first l) x)
(recur x (rest l))
(cons (first l)
(rember* x (rest l))))))
user> (rember* 'x '((x y) x (z (((((x))))))))
;; => ((y) (z ((((()))))))
(defn expel [victim xs]
(mapcat (fn [x]
(cond
(sequential? x) [(expel victim x)]
(= x victim) []
:else [x]))
xs))

CompilerException java.lang.RuntimeException: Unable to resolve symbol: invert-helper in this context

I'm pretty new to closure and I don't understand why I'm getting this error message at runtime. Here is my code:
(defn invert [lst]
(if (empty? lst)
()
(cons (invert-helper lst) (invert (rest lst)))))
(defn invert-helper [lst]
(list (nth lst 1) (first lst)))
This should fix the problem:
(defn invert-helper [lst]
(list (nth lst 1) (first lst)))
(defn invert [lst]
(if (empty? lst)
()
(cons (invert-helper lst) (invert (rest lst)))))
That is: the invert-helper function must be defined before its first use in invert.
Another option, apart from defining all the functions before using them, may be declaring invert-helper before invert:
(declare invert-helper)
(defn invert [lst]
(if (empty? lst)
()
(cons (invert-helper lst) (invert (rest lst)))))
(defn invert-helper [lst]
(list (nth lst 1) (first lst)))
You're also calling (nth lst 1) where lst may have only one element - this will throw an exception.

Clojure IndexOutOfBoundsException

((fn [coll] (letfn [(add-item [acc coll idx]
(conj acc (nth coll idx)))
(add-group [acc coll]
(conj acc (create-group coll)))
(decrease-coll [coll acc]
(drop (count (last acc)) coll))
(not-group-member? [idx coll]
(not= (first coll) (nth coll idx)))
(out-of-bounds? [idx coll]
(or (empty? coll) (> idx (count coll))))
(create-group [coll] (loop [idx 0
coll coll
acc []]
(if (or (out-of-bounds? idx coll)
(not-group-member? idx coll))
acc
(recur (inc idx) coll (add-item acc coll idx)))))
(process-coll [coll] (loop [coll coll
acc []]
(if (empty? coll)
acc
(recur (decrease-coll coll acc)
(add-group acc coll)))))]
(process-coll coll))) [1 1 2 1 1 1])
When I try to run this I receive
java.lang.IndexOutOfBoundsException: null
RT.java:795 clojure.lang.RT.nthFrom
RT.java:764 clojure.lang.RT.nth
/clojure/scratch-work-4clojure.clj:13 user/eval10378[fn]
/clojure/scratch-work-4clojure.clj:22 user/eval10378[fn]
/clojure/scratch-work-4clojure.clj:7 user/eval10378[fn]
/clojure/scratch-work-4clojure.clj:30 user/eval10378[fn]
/clojure/scratch-work-4clojure.clj:31 user/eval10378[fn]
/clojure/scratch-work-4clojure.clj:3 user/eval10378
I've been trying to debug this for some time now. I broke it into several functions to try to track down what's causing the error but haven't been able to determine it yet. Any help on what's causing this and how to debug such errors in Clojure in the future would be appreciated.
Your out-of-bounds? check is wrong. You want >= instead of >. Indexes go from 0 to n-1.

insertion sort using clojure

I ran into this error when I try to evaluate the last line below: "No message. [Thrown class java.lang.NullPointerException]"
(defn my-insertion-sort [lst]
(loop [list lst result '()]
(if-not (seq? list) result
(recur (rest list) (my-insert (first list) result)))))
(defn my-insert [n lst]
(cond (nil? lst) (list n)
(> (first lst) n) (conj lst n)
:else
(conj (my-insert n (rest lst)) (first lst))))
(my-insertion-sort '(2 1 3))
Where goes wrong with "my-insertion-sort" function?
(defn my-insertion-sort [lst]
(loop [list lst result '()]
(if (empty? list) result
(recur (rest list) (my-insert (first list) result)))))
(defn my-insert [n lst]
(cond
(empty? lst) (list n)
(> (first lst) n) (conj lst n)
:else (conj (my-insert n (rest lst)) (first lst))))

Mutation Problem - Clojure

having trouble changing an element of my function represented as a list.
code for random function:
(defn makerandomtree-10
[pc maxdepth maxwidth fpx ppx]
(if-let [output
(if (and (< (rand) fpx) (> maxdepth 0))
(let [head (nth operations (rand-int (count operations)))
children (doall (loop[function (list)
width maxwidth]
(if (pos? width)
(recur (concat function
(list (makerandomtree-10 pc (dec maxdepth)
(+ 2 (rand-int (- maxwidth 1))) fpx ppx)))
(dec width))
function)))]
(concat (list head) children))
(if (and (< (rand) ppx) (>= pc 0))
(nth parameters (rand-int (count parameters)))
(rand-int 100)))]
output
))
I will provide also a mutation function, which is still not good enough. I need to be able to eval my statement, so the following is still insufficient.
(defn mutate-5
"chooses a node changes that"
[function pc maxwidth pchange]
(if (< (rand) pchange)
(let [output (makerandomtree-10 pc 3 maxwidth 0.5 0.6)]
(if (seq? output) output (list output)))
;mutate the children of root
;declare an empty accumulator list, with root as its head
(let [head (list (first function))
children (loop [acc(list)
walker (next function)] (println "----------") (println walker) (println "-----ACC-----") (println acc)
(if (not walker)
acc
(if (or (seq? (first function)) (contains? (set operations) (first function)))
(recur (concat acc (mutate-5 walker pc maxwidth pchange))
(next walker))
(if (< (rand) pchange)
(if (some (set parameters) walker)
(recur (concat acc (list (nth parameters (rand-int (count parameters)))))
(if (seq? walker) (next walker) nil))
(recur (concat acc (list (rand-int 100)))
(if (seq? walker) (next walker) nil)))
(recur acc (if (seq? walker) (next walker) nil))))
))]
(concat head (list children)))))
(side note: do you have any links/books for learning clojure?)
Check out the clojure.walk namespace. It's likely that a function like clojure.walk/postwalk-replace is what you're looking for.