This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-05-29
Channels
- # aleph (4)
- # architecture (12)
- # bangalore-clj (1)
- # beginners (87)
- # boot (3)
- # cider (19)
- # cljs-dev (84)
- # clojars (10)
- # clojure (79)
- # clojure-italy (7)
- # clojure-nl (19)
- # clojure-russia (10)
- # clojure-spec (9)
- # clojure-uk (55)
- # clojurescript (64)
- # core-async (7)
- # core-typed (4)
- # cursive (7)
- # data-science (2)
- # datomic (8)
- # devcards (6)
- # docs (1)
- # duct (5)
- # fulcro (117)
- # graphql (1)
- # instaparse (1)
- # leiningen (13)
- # lumo (103)
- # nyc (3)
- # off-topic (54)
- # om (9)
- # onyx (1)
- # pedestal (6)
- # planck (3)
- # portkey (7)
- # re-frame (26)
- # reagent (20)
- # ring-swagger (14)
- # shadow-cljs (164)
- # sql (11)
- # tools-deps (25)
- # yada (1)
awesome
(try
(loop [user-input (read-line)] ...)
(catch ...)
(finally ... ))
how can I continue grabbing user input even when there is a catastrophic read-line error? like someone gets clever and puts in an unmatched paren (whoa did i just stumble into macro land?! sweet
haha, nevermind, just re-ran the function by name.
(loop [user-input (try (read-line) (catch Throwable _ ::bad-input))]
(if (= ::bad-input user-input)
(println "Sorry, didn't understand!")
(...)))
Is there any way to selectively unquote items inside a quoted form?
Like I want '(1 + ~x + y)
to evaluate to (1 + "blah" + y)
if x is “blah”
@hello254 Can you provide the context for that quoted expression? Is it part of a macro?
You mean like
`(1 + ~x + ~'y)
@lockdown-?user=> (let [x "blah"] `(1 + ~x + ~'y))
(1 clojure.core/+ "blah" clojure.core/+ y)
@yeh hmm, true, I would also though that `(1 ~x 3) would throw an error if x is not defined
@lockdown- In the single-quoted form, '(1 ~x 3)
would place a ~x
symbol inside the list
user> (let [x 10] `(~x y 3))
(10 user/y 3)
In syntax quoting, y
gets fully qualified. I’m trying to avoid that if possible(Also thanks to @lockdown-, just saw your earlier answer 😅 )
What's the ^:bench
in
clojure
(defn ^:bench profile-me [ms]
(println "Crunching bits for" ms "ms")
(Thread/sleep ms))
@stardiviner It’s metadata on the function equiv to ^{:bench true}
typically for something like that there's a build or dev process that loads all the namespaces and collects vars with certian metadata
this is how test selectors work, for example
I bet the project definition file would be informative
@jeremy642 Is there difference with ^:bench
before and behind function name?
Hmm, putting it before the symbol applies the meta to the symbol. You can verify this with the following.
(meta #'profile-me)
I see. Thanks
Hi, I was trying to do a infinite loop but getting Can recur from tail only. What is the correct way to do this?
(def options ["1" "2" "3" "4" "5" "6"])
(loop []
(do (println "*** Sales Menu ***")
(println "------------------")
(println "1. Display Customer Table")
(println "2. Display Product Table")
(println "3. Display Sales Table")
(println "4. Total Sales for Customer")
(println "5. Total Count for Product")
(println "6. EXIT")
(def choice (read-line))
(if (= nil (some #(= choice %) options))
((println "Please PLEASE!! Enter properly!!") (recur))
(do
(if (= choice 1 ) ((doall (for [i cust] (do (println (str (subs (str i) 2 3) ": [" (subs (str i) 5 (count (str i)))))))) (recur)))
(if (= choice 2 ) ((doall (for [i prod] (do (println (str (subs (str i) 2 3) ": [" (subs (str i) 5 (count (str i)))))))) (recur)))
(if (= choice 3 ) ((doall (for [i sales] (do (println (str (subs (str i) 2 3) ": [" (subs (str i) 5 (count (str i)))))))) (recur)))
(if (= choice 4 ) (
(do (println "Enter Customer Name: ")
(def cust_name (read-line))
(println (str cust_name ": "( cust_tot cust_name )))) (recur)))
(if (= choice 5 ) (
(do (println "Enter Product Name: ")
(def prod_name (read-line))
(println (str prod_name ": "( prod_tot prod_name )))) (recur)))
(if (= choice 6 ) (println "Good Bye"))
))))
Some general comments :
* You can use cond
to replace all do if
. Even better you can use case
case : https://clojuredocs.org/clojure.core/case.
* Repetitive println calls to print menu can be replaced with a for loop even better a print menu function.
* Try using let statements instead of defs for choice, prod_name etc since they are local bindings
* using -
is encouraged over underscores in functions like cust_tot
which reads better with customer-total
But I would still get tail error since I want an infinite loop and if I get wrong input I want to recur. The tail error happens at the first if
Some improved code. It might not compile this is how I will refactor this :
(defn print-menu
(let [menu ["*** Sales Menu ***"
"------------------"
"1. Display Customer Table"
"2. Display Product Table"
"3. Display Sales Table"
"4. Total Sales for Customer"
"5. Total Count for Product"
"6. EXIT"]]
(doseq [item menu]
(println item))))
(let [options ["1" "2" "3" "4" "5" "6"]]
(loop []
(do print-menu
(let [choice (read-line)]
(if (= nil (some #(= choice %) options))
((println "Please PLEASE!! Enter properly!!") (recur))
(case choice
1
(do
(doseq [i cust]
(do (println (str (subs (str i) 2 3) ": [" (subs (str i) 5 (count (str i)))))))
(recur))
2
(do
(doseq [i prod]
(do (println (str (subs (str i) 2 3) ": [" (subs (str i) 5 (count (str i)))))))
(recur))
3
(do
(doseq [i sales]
(do (println (str (subs (str i) 2 3) ": [" (subs (str i) 5 (count (str i)))))))
(recur))
4
(do (println "Enter Customer Name: ")
(let [customer-name (read-line)]
(println (str customer-name ": " (cust_tot customer-name)))) (recur))
5
(do (println "Enter Product Name: ")
(let [product-name (read-line)]
(println (str product-name ": " (prod_tot product-name)))) (recur))
6
(println "Good Bye")))))))
By tail error do you mean recur can be used only in tail calls or do you have some kind of stack overflow due to infinite recursion ?
((println "Please PLEASE!! Enter properly!!") (recur))
This is where I get java.lang.UnsupportedOperationException: Can only recur from tail position
You can change them to case statements and have please enter properly as the last catch all clause. Ex :
user=> (loop []
#_=> (case (read-line)
#_=> "1" (do (println "wrong") (recur))
#_=> "2" (do (println "wrong") (recur))
#_=> "3" (do (println "correct"))
#_=> (do (println "invalid option") (recur))))
1
wrong
2
wrong
5
invalid option
3
correct
nil
In you case it will be
(case choice
1 (do-for-1)
2 (do-for-2)
(do (println "Please PLEASE!! Enter properly!!") (recur))
This is not legal @kushalkanungo1991 ((println "Please PLEASE!! Enter properly!!") (recur))
-- you are invoking println
which returns nil
and then you are trying to invoke nil
as a function with (recur)
as an argument.
You need (do (println ...) (recur))
there.
Yep figured that part though, but then the rest of the if statement wont work as recur wont be at tail end. Now I am really confused about how to create infinite loop with break on condition.
The only advice I can give is to take a step back and try to go back to basics. Clojure is an expression-based language -- you're still trying to write statement-based code.
if
should never have one arm, always two. That's part of why your recur
isn't in the tail position. You have multiple if
expressions stacked up like statements.
The body of your loop
should be a single expression, not a sequence of expressions. That will let you use recur
.
You're also still trying to use for
as if it were a "for-loop" rather than a comprehension that produces a sequence. And you have additional ( .. )
wrapped around those (doall (for ...))
expressions -- again, not valid.
If you just want to loop over something and print each element of a sequence, use doseq
:
(doseq [i sales] (println ...))
Then if you wrap that with (do ... (recur))
you're a step closer.
But you also need to replace that series of separate if
expressions with a single conditional cond
and then you'll have a single expression.
My point is that you have
(if (condition)
(then-expression))
(if (condition)
(then-expression))
(if (condition)
(then-expression))
...
Multiple expressions. That's why you can't use (recur)
-- only the last expression can be "tail" position.(cond (condition) (then-expression)
(condition) (then-expression)
(condition) (then-expression))
is a single expressionClojure is expression-based not statement-based 🙂
user=> (let [x 42]
(cond (< 50 x) "big"
(< 0 x 50) "medium"
(< x 0) "negative"))
"medium"
user=>
each expression is tested until the first truthy one is found, and only that one is evaluated.Note that these are expressions, not statements.
I also just noticed that you're doing (read-line)
which gives you a string but you're testing choice
against integers -- so those will never match.
(let [choice (read-line)]
(case choice
"1" ...
"2" ...
"3" ...
"4" ...
"5" ...
"6" ...
(println "Please PLEASE!! Enter properly!!"))
(when-not (= choice "6")
(recur)))
case
takes pairs of values and expressions and then a default expression. Since you are only doing stuff for side-effects for each choice, you can do the whole thing in a case
expression (and ignore the nil
result) and then you can have a single expression which conditionally does the recur
-- and which will be in tail position.
Does that help?
Overall tho', you are trying to write Clojure in a very imperative style which really doesn't work. You have a def
in the middle of all that -- def
always defines a global variable -- you shouldn't nest def
or defn
inside another def
or defn
.
Try *'
Also (range 1 100N) returns a sequence of longs, not BigInt's. If you instead use (range 1N 100), it returns a sequence of BigInt's, and then the normal *
function will preserve them as BigInt's.
I didn't have that memorized -- just tried out (range 1 100N)
and then (range 1N 100)
in a REPL, and saw the difference in return values.
it creates a number too large to fit in a long
*' auto-promotes to bignums that can hold larger values
Is it idiomatic / problematic to do side effects in a for? My case is that, at the same time I need to return a value to create a list, I need to do something with each specific value (change an atom with swap!)
@bertofer Could you separate the effect from the computation?
it's problematic to use side effects in for, because it's lazy
does the side effect need to happen before calculating the next value?
if so, you should skip using laziness altogether and use eg. reduce or loop, if no a separate for and doseq (or run!) definitely work