Why does clojure give this arity error - clojure

I have the function definition below for map-edit
(def map-edit
(fn [m lst k f]
(if (car lst)
(assoc m
(car lst)
(map-edit (get m (car lst) {}) k f))
(assoc m k (f (get m k))))))
When I try to call this function in my repl
(map-edit {} (list "oeu") "oeuoeu" (fn [q] "oeu"))
I get an error for Arity
ArityException Wrong number of args (3) passed to: core/map-edit clojure.lang.AFn.throwArity (AFn.java:429)
Why does it think I am only passing 3 arguments?
; CIDER 0.8.2 (Java 1.8.0_121, Clojure 1.8.0, nREPL 0.2.12)

Assuming that you have these definitions
(def car first)
(def cdr rest)
The recursive call to map-edit only uses 3 arguments
The line probably should be
(map-edit (get m (car lst) {}) (cdr lst) k f))

Note
Assuming that #SultanLegend's answer is correct (not accepted as I write), the core update-in does what you require. You could define ...
(defn map-edit [m lst k f]
(update-in m (conj (vec lst) k) f))
(map-edit {} (list "oeu") "oeuoeu" (fn [q] "oeu"))
=> {"oeu" {"oeuoeu" "oeu"}}

Related

Run length encoding of sequences

So I am trying to solve this problem, and this is the code I have come up with:
First I have a pack function, receives a list and groups same elements into a vector.
(defn pack [lst]
(def a [])
(def vect [])
(cond
(empty? lst)
lst
:else
(loop [i 0]
(def r (get lst i))
(def t (get lst (+ i 1)))
(if (= r t)
(def vect (conj vect r))
)
(if (not= r t)
(and (def vect (conj vect r)) (and (def a (conj a vect)) (def vect [])))
)
(if (= i (- (count lst) 1))
a
(recur (inc i))
)
))
)
for example if I have this vector:
(def tes '[a a a a b c c a a d e e e e])
pack function will return this:
[[a a a a] [b] [c c] [a a] [d] [e e e e]]
Then I tried doing the "encode" part of the problem with this code:
(def v1 [])
(def v2 [])
(conj v2 (conj v1 (count (get (pack tes) 0)) (get (get (pack tes) 0) 0)))
And it returned what I wanted, a vector "v2" with a vector "v1" that has the "encoded" item.
[[4 a]]
So now I try to make the function:
(defn encode [lst]
(loop [index 0 limit (count (pack lst)) v1 [] v2[]]
(if (= index limit)
lst
(conj v2 (conj v1 (count (get (pack tes) index)) (get (get (pack tes) index) index)))
)
(recur (inc index) limit v1 v2)
)
)
(encode tes)
but I get this error:
2021/03/07 00:00:21 got exception from server /usr/local/bin/lein: line 152:
28 Killed "$LEIN_JAVA_CMD" "${BOOTCLASSPATH[#]}" -Dfile.encoding=UTF-8 -Dmaven.wagon.http.ssl.easy=false -Dmaven.wagon.rto=10000 $LEIN_JVM_OPTS
-Dleiningen.original.pwd="$ORIGINAL_PWD" -Dleiningen.script="$0" -classpath "$CLASSPATH" clojure.main -m leiningen.core.main "$#"
2021/03/07 01:42:20 error reading from server EOF
Any way to fix my code or to solve the problem more efficiently but still return a vector?
juxt can be used in the pack function:
(defn pack [xs]
(map (juxt count first) (partition-by identity xs)))
(defn unpack [xs]
(mapcat #(apply repeat %) xs))
Don't use def inside function, because it creates global
variable. Use let instead.
Don't use multiple if in row, there is cond.
Format your code better- for example, put all parentheses on the end together on one line.
Here is more efficient solution:
(defn pack [lst]
(letfn [(pack-help [lst]
(if (empty? lst) '()
(let [elem (first lst)]
(cons (vec (take-while #(= % elem) lst))
(pack-help (drop-while #(= % elem) lst))))))]
(vec (pack-help lst))))
(defn pack-with-count [lst]
(mapv #(vector (count %) (first %))
(pack lst)))
(defn unpack [packed-lst]
(into [] (apply concat packed-lst)))
(pack '[a a a a b c c a a d e e e e])
(pack-with-count '[a a a a b c c a a d e e e e])
(unpack '[[a a a a] [b] [c c] [a a] [d] [e e e e]])
As a rule, whenever you reach for loop/recur, there are some pieces of the standard library which will allow you to get the desired effect using higher-order functions. You avoid needing to implement the wiring and can just concentrate on your intent.
(def tes '[a a a a b c c a a d e e e e])
(partition-by identity tes)
; => ((a a a a) (b) (c c) (a a) (d) (e e e e))
(map (juxt count first) *1)
; => ([4 a] [1 b] [2 c] [2 a] [1 d] [4 e])
(mapcat #(apply repeat %) *1)
; => (a a a a b c c a a d e e e e)
Here *1 is just the REPL shorthand for "previous result" - if you need to compose these into functions, this will be replaced with your argument.
If you really need vectors rather than sequences for the outer collection at each stage, you can wrap with vec (to convert the lazy sequence to a vector), or use mapv instead of map.
Finally - the error message you are getting from lein is a syntax error rather than a logic or code problem. Clojure generally flags an unexpected EOF if there aren't enough closing parens.
(println "because we left them open like this -"
Consider working inside a REPL within an IDE, or if that isn't possible then using a text editor that matches parens for you.

Clojure filter method without standard clojure functions

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
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
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.
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?))

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))

Clojure take-while and n more items

What's the idiomatic way in Clojure to implement take-while-and-n-more below:
=> (take-while-and-n-more #(<= % 3) 1 (range 10))
(0 1 2 3 4)
My try is:
(defn take-while-and-n-more [pred n coll]
(let
[take-while-result (take-while pred coll)
n0 (count take-while-result)]
(concat
take-while-result
(into [] (take n (drop n0 coll))))))
I would use split-with, which is equivalent of getting results of both take-while and drop-while for the same parameters:
(defn take-while-and-n-more [pred n coll]
(let [[head tail] (split-with pred coll)]
(concat head (take n tail))))
Yet another way:
(defn take-while-and-n-more [pred n coll]
(let [[a b] (split-with pred coll)]
(concat a (take n b))))
The following code is a modified version of Clojures take-while. Where Clojures take-while returns nil as a default case (when the predicate does not match), this one invokes take to take the the additional items after the predicate fails.
Note that unlike versions using split-with, this version traverses the sequence only once.
(defn take-while-and-n-more
[pred n coll]
(lazy-seq
(when-let [s (seq coll)]
(if (pred (first s))
(cons (first s) (take-while-and-n-more pred n (rest s)))
(take n s)))))

Defining my own max function with variable arguments

I'm learning Clojure solving the problems listed on 4clojure. One of the exercises is to create your own max function with variable arguments.
I'm trying to solve this easy problem using the REPL and I got to this solution:
(defn my-max
[first & more] (calc-max first more))
(defn calc-max
[m x]
(cond (empty? x) m
(> (first x) m) (calc-max (first x) (rest x))
:else calc-max m (rest x)))
Which works fine but the exercise doesn't allow the use of def and therefore I must crunch both functions into one. When I replace the calc-max reference with its code the result is:
(defn my-max
[first & more]
((fn calc-max
[m x]
(cond (empty? x) m
(> (first x) m) (calc-max (first x) (rest x))
:else calc-max m (rest x)))
first more))
But this code doesn't work and returns the next error:
user=> (my-max 12 3 4 5 612 3)
java.lang.ClassCastException: java.lang.Integer cannot be cast to clojure.lang.IFn (NO_SOURCE_FILE:0)
I guess this error comes from trying to evaluate the result of the calc-max function and I guess it's a syntax error on my part, but I can't figure out how to resolve it.
Here is the function I used to solve it. The point is not to use max at all.
(fn [& args] (reduce (fn [x y] (if (> x y) x y) ) args ) )
Real error is that you called parameter first - it rebinds real first function to number! Just change name to something other, and your variant will work. Although it maybe better explicitly name function, instead of calling anonymous function, for example, you can declare calc-max as local function using letfn, for example. So your my-max will look like:
(defn my-max [ff & more]
(letfn [(calc-max [m x]
(cond (empty? x) m
(> (first x) m) (calc-max (first x)
(rest x))
:else (calc-max m (rest x))))]
(calc-max ff more)))
Although, I think, that you can write simpler code:
(defn my-max [& more] (reduce max more))
Your function doesn't work because first in fn treated as function and not as input value. So when you write
user=> (my-max 12 3 4 5 612 3)
it's talling that can't cast 12 to function. Simply, it can be rewrited as
(defn my-max1 [fst & more]
((fn calc-max [m x]
(cond (empty? x) m
(> (first x) m) (calc-max (first x) (rest x))
:else (calc-max m (rest x))))
fst more))
or even without fn
(defn my-max [x & xs]
(cond (empty? xs) x
(> (first xs) x) (recur (first xs) (rest xs))
:else (recur x (rest xs))))
To elaborate a little more on the exception that you are seeing: whenever Clojure throws something like
java.lang.Integer cannot be cast to clojure.lang.IFn
at you it means that it tried to call a function but the thing it tried to call was not a function but something else. This usually occurs when you have code like this
(smbl 1 2 3)
If smbl refers to a function, clojure will execute it with parameters 1 2 and 3. But if smbl doesn't refer to a function then you will see an error like the one above. This was my pointer in looking through your code and, as 4e6 pointed out, (first x) is the culprit here because you named your function argument first.
Not as good as the reduce but ok ba:
(fn [& args]
(loop [l args, maxno (first args)]
(if (empty? l)
maxno
(if (> maxno (first l) )
(recur (rest l) maxno)
(recur (rest l) (first l))))))
Can use cond I suppose