I have some code which I refactored only to find out something was broken with loop. After some debugging I found out loop and with-redefs do not play well together. I realize it may not make sense to use with-redefs inside a loop, but I didn't expect it to not work. I'm not sure if its intentional or not.
This is an MCVE I've created to demonstrate the "problem":
(loop [test 3]
(with-redefs []
(if (zero? test)
"done"
(recur (dec test)))))
This gives me:
Mismatched argument count to recur, expected: 0 args, got: 1
Removing the with-redefs works as expected:
(loop [test 3]
(if (zero? test)
"done"
(recur (dec test))))
and returns "done".
What is the reason the first piece of code does not work? Is this intentional?
The explanation is in the macroexpansion of with-redefs:
(macroexpand-1
'(with-redefs []
(if (zero? test)
"done"
(recur (dec test)))))
returns:
(with-redefs-fn {}
(fn []
(if (zero? test)
"done"
(recur (dec test)))))
where you can see that because a new fn has been introduced, the recur is going to refer to that fn rather than the farther-away loop (which explains the arity exception).
There are a variety of other macros that are "incompatible" with loop in this way, because the recur needs to be in the tail position with respect to loop, and if the recur occurs inside a macro call, the macro may be manipulating the code such that the recur is no longer in tail position.
For with-redefs in particular (and a variety of other situations), a workaround could be:
(loop [test 3]
(let [[recur? val]
(with-redefs []
(if (zero? test)
[false "done"]
[true (dec test)]))]
(if recur?
(recur val)
val)))
Related
I made a function to take questions like this.
(defn ask-ques [ques pred]
(print ques)
(let [user-input (read-line)]
(if #(pred user-input) user-input (recur ques pred))))
And I wrote main like this.
(defn -main []
(loop []
(let [user-input (ask-ques "CHOOSE ONE. (C)ontinue OR (E)xit : " #(contains? #{"C" "E"} %))]
(when (= user-input "C") (apply body (rand-nth (seq voc-map))) (recur)))))
But, Clojure received the input first and printed "CHOOSE ONE. (C)ontinue OR (E)xit : " out, and pred does not work well.
What's the problem? Why does it work like this? And what should I do?
#(pred user-input) is a function of zero arguments and, since it has a non-nil value, the if will treat it as truth, so you will always get user-input and it will never recur. I suspect you want (pred user-input) instead.
I need to write a Clojure function which takes an unevaluated arbitrarily deep nesting of lists as input, and then determines if any item in the list (not in function position) is non-numeric. This is my first time writing anything in Clojure so I am a bit confused. Here is my first attempt at making the function:
(defn list-eval
[x]
(for [lst x]
(for [item lst]
(if(integer? item)
(println "")
(println "This list contains a non-numeric value")))))
I tried to use a nested for-loop to iterate through each item in every nested list. Trying to test the function like so:
=> (list-eval (1(2 3("a" 5(3)))))
results in this exception:
ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn listeval.core/eval7976 (form-init4504441070457356195.clj:1)
Does the problem here lie in the code, or in how I call the function and pass an argument? In either case, how can I make this work as intended?
This happens because (1 ..) is treated as calling a function, and 1 is a Long, and not a function. First you should change the nested list to '(1(2 3("a" 5(3)))). Next you can change your function to run recursively:
(defn list-eval
[x]
(if (list? x)
(for [lst x] (list-eval lst))
(if (integer? x)
(println "")
(println "This list contains a non-numeric value"))))
=> (list-eval '(1(2 3("a" 5(3)))))
There is a cool function called tree-seq that does all the hard work for you in traversing the structure. Use it then remove any collections, remove all numbers, and check if there is anything left.
(defn any-non-numbers?
[x]
(->> x
(tree-seq coll? #(if (map? %) (vals %) %))
(remove (some-fn coll? number?))
not-empty
boolean))
Examples:
user=> (any-non-numbers? 1)
false
user=> (any-non-numbers? [1 2])
false
user=> (any-non-numbers? [1 2 "sd"])
true
user=> (any-non-numbers? [1 2 "sd" {:x 1}])
true
user=> (any-non-numbers? [1 2 {:x 1}])
false
user=> (any-non-numbers? [1 2 {:x 1 :y "hello"}])
true
If you want to consider map keys as well, just change (vals %) to (interleave (keys %) (vals %)).
quoting
As others have mentioned, you need to quote a list to keep it from being evaluated as
code. That's the cause of the exception you're seeing.
for and nesting
for will only descend to the nesting depth you tell it to. It is not a for loop,
as you might expect, but a sequence comprehension, like the the python list comprehension.
(for [x xs, y ys] y) will presume that xs is a list of lists and flatten it.
(for [x xs, y ys, z zs] z) Is the same but with an extra level of nesting.
To walk down to any depth, you'd usually use recursion.
(There are ways to do this iteratively, but they're more difficult to wrap your head around.)
side effects
You're doing side effects (printing) inside a lazy sequence. This will work at the repl,
but if you're not using the result anywhere, it won't run and cause great confusion.
It's something every new clojurian bumps into at some point.
(doseq is like for, but for side effects.)
The clojure way is to separate functions that work with values from functions that
"do stuff", like printing to the console of launching missiles, and to keep the
side effecting functions as simple as possible.
putting it all together
Let's make a clear problem statement: Is there a non number anywhere inside an
arbitrarily nested list? If there is, print a message saying that to the console.
In a lot of cases, when you'd use a for loop in other langs reduce is what you want in clojure.
(defn collect-nested-non-numbers
;; If called with one argument, call itself with empty accumulator
;; and that argument.
([form] (collect-nested-non-numbers [] form))
([acc x]
(if (coll? x)
;; If x is a collection, use reduce to call itself on every element.
(reduce collect-nested-non-numbers acc x)
;; Put x into the accumulator if it's a non-number
(if (number? x)
acc
(conj acc x)))))
;; A function that ends in a question mark is (by convention) one that
;; returns a boolean.
(defn only-numbers? [form]
(empty? (collect-nested-non-numbers form)))
;; Our function that does stuff becomes very simple.
;; Which is a good thing, cause it's difficult to test.
(defn warn-on-non-numbers [form]
(when-not (only-numbers? form)
(println "This list contains a non-numeric value")))
And that'll work. There already exists a bunch of things that'll help you walk a nested structure, though, so you don't need to do it manually.
There's the clojure.walk namespace that comes with clojure. It's for when you have
a nested thing and want to transform some parts of it. There's tree-seq which is explained
in another answer. Specter is a library which is
a very powerful mini language for expressing transformations of nested structures.
Then there's my utils library comfy which contains reduce versions of the
functions in clojure.walk, for when you've got a nested thing and want to "reduce" it to a single value.
The nice thing about that is that you can use reduced which is like the imperative break statement, but for reduce. If it finds a non-number it doesn't need to keep going through the whole thing.
(ns foo.core
(:require
[madstap.comfy :as comfy]))
(defn only-numbers? [form]
(comfy/prewalk-reduce
(fn [ret x]
(if (or (coll? x) (number? x))
ret
(reduced false)))
true
form))
Maybe by "any item in the list (not in function position)" you meant this?
(defn only-numbers-in-arg-position? [form]
(comfy/prewalk-reduce
(fn [ret x]
(if (and (list? x) (not (every? (some-fn number? list?) (rest x))))
(reduced false)
ret))
true
form))
I am new to Clojure and I am currently stuck with below code which throws a NullPointerException when I run it like this:
(mapset inc [1 1 2 2])
(defn mapset
[fn v]
(loop [[head & tail] v result-set (hash-set)]
(if (nil? head)
result-set)
(conj result-set (fn head))
(recur tail result-set)))
When I print the result-set in the if block it prints an empty set whereas I expect a set like this: #{2 3}
After trying to interpret the stacktrace I guess that the NullPointerException has something to do with the following line:
(conj result-set (fn head)).
The stacktrace and the fact that the resulting set is empty leads me to believe that the inc operation somehow gets called with nil as input.
I am glad about any explanation of why this error happens
The mentioned (shortend) stacktrace looks like this:
java.lang.NullPointerException
Numbers.java: 1013 clojure.lang.Numbers/ops
Numbers.java: 112 clojure.lang.Numbers/inc
core.clj: 908 clojure.core/inc
core.clj: 903 clojure.core/inc
REPL: 6 user/mapset
REPL: 1 user/mapset
I made some small changes:
(defn mapset [f v]
(loop [[head & tail] v
result-set (hash-set)]
(if (nil? head)
result-set
(let [res (f head)]
(recur tail (conj result-set res))))))
(defn x-1 []
(mapset inc [1 1 2 2]))
(x-1)
;;=> #{3 2}
So now mapset will call the function f on each of your inputs from v, then put the result of that call into the hash-set that was created at the outset.
The problem was in the control flow logic of your if statement. The flow of execution was continuing on after the if. Thus the function fn (which I renamed to f, rather than leave it as the name of a macro) was being called even when head was nil, which was not what you intended.
As originally coded the if (which would more clearly have been a when) was doing nothing useful. But once I realised you meant to have an if, but closed the paren off too early, then the answer slotted into place. Hence minor changes fixed the problem - your underlying logic was sound - and the amended function just worked.
Why doesn't the following snippet of code print "1" to the console?
(loop
[i 0]
(println (if (= i 0) (recur 1) i)))
Instead, it throws clojure.lang.ExceptionInfo: Can't recur here at line 3 in the REPL. Are nested (recur..) statements like this not allowed in Clojure(Script)?
Correct, "nested recur statements" are not allowed in any Clojure dialect. The alternate behavior described in a comment (recur "halts and dismisses the execution of its parent statements") would work as an alternate language design choice, but would probably be a lot more confusing to read.
In your code you try to print the result of (recur 1), which doesn't make sense. I think you probably meant to do (recur 1) if i==0, and print i otherwise, as follows:
(loop [i 0]
(if (= i 0) (recur 1) (println i)))
See Clojure: What exactly is tail position for recur? - you can only use recur from a "tail position" in Clojure. In this case, recur is not in a tail position because it is not the last thing to be evaluated in this function - the println would be evaluated after the call to recur.
Is it possible to use both recur and post-condition functionality in the same Clojure function? I was hoping to throw an exception using the post-condition, but Clojure appears to be trying to wrap the exception throwing code after the recur somehow, so (just as a stupid example) functions like this cannot be evaluated.
(defn countup [x]
{:pre [(>= x 0)]
:post [(>= % 0)]}
(if (< x 1000000)
(recur (inc x))
x))
I'm using Clojure 1.3 at the moment.
If you look at the implementation of defn at https://github.com/clojure/clojure/blob/35bd89f05f8dc4aec47001ca10fe9163abc02ea6/src/clj/clojure/core.clj#L4580-L4585 you'll see that the body of a function gets modified so that tail calls get pushed out of the tail-position. One way around this is to use an auxiliary function to call the recur'd function and put the post-condition on that instead:
(defn- countup* [x]
(if (< x 1000000)
(recur (inc x))
x))
(defn countup [x]
{:pre [(>= x 0)]
:post [(>= % 0)]}
(countup* x))
(countup 999999)
;=> 1000000
(countup -1)
; Assert failed: (>= x 0)
"recur" is like "goto to the begin of the block with that parameters".
You cannot place any code after it because of it does not save things in the stack, so don't know where it came from (and what checks it should perform after running).
For example, (loop [] (recur)) will loop forever without consuming stack.
In your example I expect :post to be executed once, when x==1000000.
You can totally do this and in many cases the ability to get pre/post conditions may be worth the speed lost by not using the loop/recur special form.
The recur special form wont do it for ya because it is not really a function call. You could make your own wrapper function that does the bounds checking in pre and post conditions after all the code runs, or you can use the built in trampoline function and save a little effort and have the conditions checked on every iteration (you need to decide if you want that)
You can turn this into a recursive function with out blowing the stack:
(defn countup [x]
{:pre [(>= x 0)]
:post [(or (ifn? %) (>= % 0))]}
(if (< x 1000000)
#(countup (inc x))
x))
(trampoline (countup 0))
1000000
This changes the post condition to ignore the intermediate cases (where it returns the function to run next) and only verify the final result.
The idea behind trampolining is to avoid blowing the stack by having each iteration of the function return the call to the next function instead of calling it directly. this way one two stack frames are used (one for the trampoline and one for the current step)
IMHO the easiest (no trampolining or using auxiliary functions) would be something like this:
(defn countup [x]
{:pre [(>= x 0)]
:post [(>= % 0)]}
(loop [x x]
(if (< x 1000000)
(recur (inc x))
x)))
(countup 999999)
1000000
(countup -1)
; Evaluation aborted.
So, just inject an auxiliary loop (identical to the function signature).