You mean removing
(loop [stack stack]
?
I don't think it's needed, actually. recur
target both loop
and functions, so the loop is redundant here.If I have two different kind of output like vector or map -> like in this topic https://gist.github.com/damesek/d6a371ce56983e77872cd7a574f548bb is that possible use comp
in some way? I can solve with two different function or not so nice solution like this.. but is that possible nicer?
(reduce (-transform-to-map)
{}
(reduce (-vec-frequencies)
[]
data))
(.indexOf …) is O(n) so watch out if you’re applying this to large collections, it can be quite slow.
True, I totally forget.. thanks!
Is there any way for me to use java methods as predicate functions in Clojure? I just discovered that while the first function works, the second doesnt:
(defn letters? [s]
(not= (count (filter #(if (Character/isLetter %)
true
false) s))
0))
(defn letters2? [s]
(some Character/isLetter s))
@georghagen Just wrap in a function: (some #(Character/isLetter %) s))
Thanks 😃 I end up with this because nil is truthy, and more idiomatic way to write it? (defn letters? [s] (if (some #(Character/isLetter %) s) true false))
Do you even need the “if” statement?
Oh, I think I see. You don’t want the truth/falsy values returned, you want it coerced.
That's right because it needs to be like that for the predicate function to work in some right?
(def letters? (partial some #(Character/isLetter %)))
if you want to be fancy ;)
I usually still write the defn
out in full though (better static analysis for clj-kondo for example)
Ahh ok, yeah it gets to be a point where you can just write it out, Clojure is very terse to begin with also so 😃
if you are doing (if test true false)
, can you replace this with just (boolean test)
Or am I misunderstanding?
correct
Hey everyone, Is there anyway to convert "false"
string to Boolean in Clojurescript?
(let [s "false"
t-or-f (cljs.tools.reader.edn/read-string s)]
(println [t-or-f (type t-or-f)]))
=>
[false #object[Boolean]]Hi. How do I traverse this kind of data structure and return a value of total minutes for each key :
(defn time-regs
[]
{"<mailto:some@example.org|some@example.org>" [{:user_id 1, :email "<mailto:some@example.org|some@example.org>", :time-registered "0 hrs, 0 min"}
{:user_id 1, :email "<mailto:some@example.org|some@example.org>", :time-registered "2 hrs, 20
min"}],
"<mailto:other@example.org|other@example.org>" [{:user_id 3, :email "<mailto:other@example.org|other@example.org>", :time-registered "0 hrs, 20
min"}
{:user_id 3, :email "<mailto:other@example.org|other@example.org>", :time-registered "1 hrs, 20
min"}]})
I want to traverse :time-regs and for every key ("<mailto:some@example.org|some@example.org>", "<mailto:other@example.org|other@example.org>") evaluate :time-registered with :
match (re-matches #"([0-9]+) hrs, ([0-9]+) min" time-reg)
and if there is a match, add minutes.
Finally, I want to return something like :
{"<mailto:some@example.org|some@example.org>" [:min-total 140]
"<mailto:other@example.org|other@example.org>" [:min-total 100"]}
I have looked at doseq - it makes it possible to access my keys and values, but doesn't return a value.
I have tried with map : (map #(% 0) (time-regs)), but can't find a way to access the values for the keysThis is how to access the values of the keys:
(->> (time-regs)
(map
(fn [[k vs]]
[k [:min-total (mapv :time-registered vs)]]))
(into {}))
=>
{“<mailto:some@example.org|some@example.org>” [:min-total [“0 hrs, 0 min” “2 hrs, 20 min”]],
“<mailto:other@example.org|other@example.org>” [:min-total [“0 hrs, 20 min” “1 hrs, 20 min”]]}I am not sure how to reliably convert things like “2 hrs, 20 min” to minutes; perhaps there’s a better format to return this in? If not, I’d look into date/time libraries that might support this kind of thing; ideally I’d like to avoid it and rely on something like a timestamp/date.
This question is about style. I wrote a function like this: (defn my-func [some-arg] (let [ x1 (whatever ...) x2 (whatever ...) y1 (whatever ...) y2 (whatever ...)] (fn [x] ( whatever)))) The point is to solve a boundary value problem, then return a function that is the solution to that problem. Here's my question: Am I guilty of writing C in Clojure?
Thanks a lot for your answer 🙂. True about the time format (it's others code that I'm trying to test)
You can use this function to convert the string-based :time-registered format to minutes
(defn time-registered->min-total
[time-registered]
(when-let [[_ hrs min] (re-matches #"(.+) hrs, (.+) min" time-registered)]
(+ (* (Integer/parseInt hrs) 60)
(Integer/parseInt min))))
(time-registered->min-total "2 hrs, 39 min")
;;=> 159
Sorry, it's not clear to me what you're doing. Where are you using those bindings? What about some-arg? What do you think is C-like about your example? Apart from your question, I think it's more common to start the bindings on the let line:
(defn my-func [some-arg]
(let [x1 (whatever ...)
x2 (whatever ...)
y1 (whatever ...)
y2 (whatever ...)]
(fn [x] (whatever))))
I've seen @borkdude use _
as let symbols to just "collect side effcts". Something like
(defn my-func [some-arg]
(let [x1 (whatever ...)
_ (whatever ...)
y1 (whatever ...)
_ (whatever ...)]
(fn [x] (whatever))))
if x2 and y2 are never used.
You could see if you could refactor your functions for use in a pipeline (`->>` or ->
)> Here's my question: Am I guilty of writing C in Clojure? Sure, that looks a lot like C. But Clojure is flexible for a reason. Writing imperative code in Clojure is fine, if you need imperative code.
What the function does is take 2 tuples (x1, y1), (x2, y2), and solve y=mx+b. Then return a function representing the y=mx+b.
Yeah I can tidy up the indents a bit
@teodorlu yeah Haskell you hear that???
I like to use a style like that to write “self-documenting” code--breaking the intermediate values out onto separate lines in a let
allows you to assign them meaningful and informative names that will help future devs in understanding what exactly the function is doing.
Sometimes I’ll write an expression that uses the ->
threading macro, and then later I’ll come back and I have a hard time following the flow, so I’ll convert it back to a let
statement with meaningful names for the intermediate values.
@manutter51 yeah, that makes sense
I want to implement the following function in Clojure:
(facts "about `to-namedlist`"
(to-namedlist {:plot "quals.svg" :data "quals.tsv"}) => {:plot "quals.svg" :data "quals.tsv" 0 "quals.svg" 1 "quals.tsv"}
(to-namedlist ["bwa-map.bam" "bwa-map2.bam"]) => {0 "bwa-map.bam" 1 "bwa-map2.bam"}
(to-namedlist "hg19.fasta") => {0 "hg19.fasta"})
It seems like I need to switch on the type of the input arg to do it. What is the most idiomatic way to achieve this?I agree 🙂
also, you can use defmulti
and use type
as your dispatch function, that way you can add support for more types in a more elegant way, and defmulti automatically does proper subclass dispatch
(cmd)user=> (defmulti foo type)
nil
(cmd)user=> (defmethod foo java.util.Map [_] :map)
#object[clojure.lang.MultiFn 0x2e52fb3e "clojure.lang.MultiFn@2e52fb3e"]
(cmd)user=> (foo {})
:map
(ins)user=> (foo (java.util.HashMap.))
:map
Thanks. I guess (case (type arg) ...
would also be acceptable here then?
I know the below is abominable code... But even worse, it does not work as expected:
(defn switch-on-type [arg]
(case (str (type arg))
"java.lang.String" "String!!!"
"clojure.lang.PersitentArrayMap" "Map!"
"Dunno"))
(switch-on-type "hi") ; "Dunno" ;; I wanted "String!!!"
Try making the last line (str "Dunno what " (type arg) " is.")
, it’ll help you pin down the problem.
Thanks for the suggestion. I am still unable to see my error. Btw, you see that I use (str (type arg))
in my case
, right?
using next.jdbc
,
how would it be possible to do batch-upsert (with column insertion), something like this: (this would work using java.jdbc)
(jdbc/execute! db ["INSERT INTO T(col1, col2)
VALUES (?, ?)
ON DUPLICATE UPDATE
col2=values(col2)"
[["c1" "c2"]
["c3" "c4"]]....more here] {:multi? true})
Yes, but including it in the last line should show you what you're getting that you're not expecting. What's it returning if you add that?
I still do not see my error... 😖 :duncecap:
why do you need to check the type
explicitly?
something like this is much clearer and likely meets your needs?:
(cond
(string? arg) "String!!"
(map? arg) "Map!"
:else "dunno")
For some reason I was looking for a hash-map?
but did not find it. It's been too long since I've used Clojure 😕
hash-map
is a fn, so i can see the confusion 🙂
glad to help
Yes, thank you 🙂
How does one typically document that a function throws an exception and which (if applicable)?
Most common in the standard lib is simply "Throws <exception> when <condition>" in the docstring.
Trying to think of an example, but spacing
Is currying considered idiomatic Clojure? If so, is there a rule of thumb for how to order the inputs in a function?
Gotcha. Pretty barebones but to the point, makes sense.
I see next.jdbc
has execute-batch!
(https://cljdoc.org/d/com.github.seancorfield/next.jdbc/1.1.643/api/next.jdbc#execute-batch!) and insert-multi!
(https://cljdoc.org/d/com.github.seancorfield/next.jdbc/1.1.643/api/next.jdbc.sql#insert-multi!). Maybe one of those can help you. Otherwise I'd ask in #sql 🙂
No
yep, wanted to escape the "prepared-statement", but I guess i'd go with that now -- thanks!
Ok cool
There is partial
but I rarely see it used, compared to eg ML languages. Since it’s so easy to create a lambda and that does away with the issue of input ordering I personally haven’t really felt that I’m missing it as an idiomatic approach.
I seem to recall Rich has said that he considers short, anonymous functions to be more idiomatic than partial
(a long time ago, on the Clojure Google Groups mailing list).
This pragmatic approach is great and I’m happy to see it championed among clojurists. A lot of functional heavy languages also tend to cultivate a culture where terse compositions / pipelines are preferred and while I can see the appeal of elegance I think it can easily create hard to maintain code.
re arg order, see https://clojure.org/guides/faq#arg_order
Adding another datapoint, I started off with terse code and after my first time working with others on a Clojure project, shifted to writing more verbose let
binding blocks to improve readability. The trick for using _
for side-effecting steps within a let is also something I'll lean on, I think the code is much cleaner that way than using multiple lets
, though if there are lots of them I consider it a code smell that has me looking to see if there is a better structure
There can be multiple different concrete types implementing each of these kinds of collections, e.g. PersistentHashMap and PersistentArrayMap for maps, plus a handful of others that are in less common use.
If all you care about is that it implements the Clojure map interface, then (map? x)
is better than listing multiple concrete types, probably, for long term code maintenance.
When using (print), is it possible to suppress the "nil" that follows the printout?
the nil that follows is the repl printing out the result of the call to print
The compiled app would not print it?
code run outside of a repl won't
Ok. Then my fizzbuzz works 🙂
I was trying to figure out how to remove that nil for a while now 😄
Hmmh, it works in REPL but I can't seem to get the complied version to work. What is the correct syntax to pass on the CLI attributes to the -main function? This is what I have currently:
(ns test-world2.core
(:gen-class))
(defn -main [& args]
(defn fizz-buzz-full [n]
(cond
(and (zero? (mod n 3)) (zero? (mod n 5))) (println "fizzbuzz")
(zero? (mod n 3)) (println "fizz")
(zero? (mod n 5)) (println "buzz")
:else (println n)))
(for [x (range 1 args)]
(fizz-buzz-full x)
)
)
(-main)
lein run 101 just fails with " Exception in thread "main" Syntax error compiling at (test_world2/core.clj:19:1)." 19 being the line for (-main).
couple things. don't nest defn
. maybe you have experience with scheme? Just put (defn fizz-buzz [n] ...)
outside of your main function. arguments passed from the command line are going to be strings. so you'll have "101"
not 101
to contend with. If you're invoking (-main)
with no args in your namespace it will call that with no args which will hit the code (range 1 nil)
which will probably blow up. Can you post the error you've gotten here in a snippet?
Ok, I've broken the other function out from -main and added "(int args)" to avoid the string-issue:
(ns test-world2.core
(:gen-class))
(defn fizz-buzz-full [n]
(cond
(and (zero? (mod n 3)) (zero? (mod n 5))) (println "fizzbuzz")
(zero? (mod n 3)) (println "fizz")
(zero? (mod n 5)) (println "buzz")
:else (println n)
)
)
(defn -main [& args]
(for [x (range 1 (int args))]
(fizz-buzz-full x)
)
)
;(-main)
This resulted in a new error (yay!) on "Unable to resolve symbol: fizz-buzz-full in this context"
The full error message is very long
Yes. The function needs to be defined before it’s call. You can use triple backticks to have code blocks
If I comment out the (-main)
- as my understanding is that lein run
will run (-main)
automatically? I get an error on " class clojure.lang.ArraySeq cannot be cast to class java.lang.Character (clojure.lang.ArraySeq is in unnamed module of loader 'app'; java.lang.Character is in module java.base of loader 'bootstrap')"
I guess I'm handling the cli arguments in the wrong way.
I updated the code block above to match the current version
Yeas the &args there should be some kind of sequence of strings. Most likely (“101”).
Do you have a repl running?
Yeah, in VS code
I get the same error if I uncomment (-main) and change it to (-main 101) and try to evaluate it.
Do you know how to send forms to the repl from your text file?
Yes, the code worked flawlessly until I added the option of defining the range
Ok. So again & args will take all of the arguments in as a sequence of args. And you can’t call int on a collection
And it might be better to call main with “101” because that’s what it will look like when you call it from the command line
So this works in REPL:
(ns test-world2.core
(:gen-class))
(defn fizz-buzz-full [n]
(cond
(and (zero? (mod n 3)) (zero? (mod n 5))) (println "fizzbuzz")
(zero? (mod n 3)) (println "fizz")
(zero? (mod n 5)) (println "buzz")
:else (println n)))
(defn -main [args]
(for [x (range 1 (int args))]
(fizz-buzz-full x)
)
)
(-main 101)
I dropped the & from the [args]
But if I comment out (-main 101) and run lein run 101 I still get the error.
Do I need to touch some of the other project files to somehow enable the arguments getting passed?
I'm very, very new to clojure and lein
your problem is with the way you parse args in your main function
but that's not a valid signature for a main function
Ok, what does signature mean in this context? Is the -main function special?
now a common thing to do is make a main that has the normal & args
function, take the first arg, turn it into a numeric type rather than a string, and then call your function. and this way your function can just deal with a single numeric and do what it wants and ignore strings, and collections, and other ugliness
yes. its an entrypoint and takes a collection of strings. that's how it can be invoked from lein run
. it kinda has to have that shape. but let's ignore that for now. write a function that takes a single long and does your fizz buzz stuff on it. practice it at the repl (fizzbuzz 7)
does what you want it to do and then we can hook up the main function to that
Will do. Though tomorrow - it's past 2300 here. Thank you for your help and patience.
Thank you gents!
🙂