This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-06-18
Channels
- # announcements (6)
- # babashka (16)
- # beginners (194)
- # calva (20)
- # cider (34)
- # clj-kondo (38)
- # clojure (89)
- # clojure-europe (10)
- # clojure-finland (15)
- # clojure-italy (2)
- # clojure-nl (15)
- # clojure-sg (7)
- # clojure-spec (15)
- # clojure-uk (86)
- # clojurebridge (1)
- # clojurescript (60)
- # community-development (11)
- # conjure (13)
- # core-async (48)
- # core-typed (3)
- # cursive (22)
- # datascript (8)
- # datomic (40)
- # duct (11)
- # emacs (3)
- # figwheel-main (22)
- # fulcro (45)
- # graphql (1)
- # helix (9)
- # hoplon (15)
- # hugsql (7)
- # jobs-discuss (47)
- # juxt (7)
- # kaocha (21)
- # luminus (1)
- # malli (13)
- # meander (2)
- # off-topic (52)
- # parinfer (19)
- # re-frame (66)
- # reagent (1)
- # reitit (3)
- # ring-swagger (1)
- # rum (2)
- # shadow-cljs (72)
- # spacemacs (5)
- # sql (4)
- # timbre (5)
- # tools-deps (15)
- # vim (5)
- # vrac (7)
If i have a list of maps, e.g.
({:b10 1, :b2 "1"}
{:b10 3, :b2 "11"}
{:b10 5, :b2 "101"}
{:b10 7, :b2 "111"}
{:b10 9, :b2 "1001"}
{:b10 33, :b2 "100001"}
{:b10 99, :b2 "1100011"})
Is their a way to use reduce to add up all the values of :b10 without having to do something like (map #(:b10 %) my-list)
first ?sure, with a reducing function like (fn [acc m] (+ acc (:b10 m)))
and the optional init arg (0 of course)
of course (apply + (map ...))
with your original version does the same thing
also :b10
and #(:b10 %)
do the same thing for the one arg case (in fact for all non-macro f
, f
and #(f %)
are the same for the one arg case)
(time (->> (range 1 1000000)
(map (fn [x] {:b10 x :b2 (Integer/toString x 2)}))
(filter (fn [x] (and (is-palindrom (:b2 x)) (is-palindrom (:b10 x)))))
(reduce (fn [acc m] (+ acc (:b10 m))) 0)))
"Elapsed time: 328.579422 msecs"
=> 872187
And
(defn find-base2-base10-palindromes-below-1-million [^Integer sum, ^Integer cur-base-10]
(if (= cur-base-10 1000000)
sum
(if (and (is-palindrom cur-base-10) (is-palindrom (Integer/toString cur-base-10 2)))
(recur (+ sum cur-base-10) (inc cur-base-10))
(recur sum (inc cur-base-10)))))
(time (find-base2-base10-palindromes-below-1-million 0 1))
"Elapsed time: 136.254228 msecs"
=> 872187
(defn is-palindrom [x]
(= (str x) (str/reverse (str x))))
Should I just expect the second one to be faster? Is their something I can read / learn to be able to write code in the first version, but have it be as quitck as the second version, or is that just not achievable?What about transduce
here? Even tho' you'd have (map :b10)
it would do it all in one non-lazy operation.
Cause I like the syntax in the first version better, but when I do that its more than twice as slow. @seancorfield transducers can make the first version faster?
the likely performance difference is in the allocation / navigation of the lazy-seqs
which transducers do avoid
your first example should be pretty easy to turn into transduce (transduce (comp (map ...) (filter ...) (map :b10)) + (range 1 100000))
In the first example then, is it creating intermediate collections between map and filter?
and unlike transduce, it actually creates a collection from range (transduce is clever enough to know it doesn't have to)
also to be pedantic, x
isn't base 10, it's a number, clojure's default print of it is base 10, but numbers don't have bases, strings do
(unless one counts the in-memory representation, which is very rarely anything but base 2)
Just curious if I am reinventing the wheel here or it could be simplified:
(defn stitch
"Concatenates a list of strings without leaving extra spaces."
[& args]
(let [vals (remove empty? args)]
(when (not-empty vals)
(reduce #(str %1 " " %2) vals))))
These are passing:
(deftest stitch-test
(testing "Concatenating without extra spaces."
(is (= (stitch (when false "one")) nil))
(is (= (stitch "has-icons-left") "has-icons-left"))
(is (= (stitch "has-icons-left" "has-icons-right") "has-icons-left has-icons-right"))))
@somedude314 That feels like (str/join " " (keep seq args))
but I'd have to test that in the REPL to be sure.
Ah, not quite. (filter seq args)
user=> (clojure.string/join " " (filter seq ["Hello" "" "World" "!" "" "and" "more"]))
"Hello World ! and more"
user=>
@somedude314 args
would just replace the vector. It's "just" a sequence.
user=> (defn foo [& args] (->> args (filter seq) (clojure.string/join " ") (not-empty)))
#'user/foo
user=> (foo "this" "and" "that")
"this and that"
user=> (foo "what" "" "about" "" "this")
"what about this"
user=> (foo "" "" "")
nil
user=>
Is that what you're looking for?
Although empty sequences and nil
both return an empty string (which seems more intuitive to me):
user=> (clojure.string/join " " (filter seq []))
""
user=> (clojure.string/join " " (filter seq nil))
""
user=>
It actually must be nil because otherwise Hiccup displays the property (empty CSS class)
(->> args (filter seq) (clojure.string/join " ") (not-empty))
yup, that would work.
Here’s a solution via transducers:
(transduce
(map identity)
(fn
;init (called at the start)
([] nil)
;reducing...
([accum item] (str accum " " item))
;completing (called at the end)
([ret]
(if (string? ret)
(clojure.string/trim ret)
ret)))
["qwe" "asd"])
@U050KSS8M it fails with ["qwe" "" nil "asd"]
(transduce
(comp
(filter string?)
(remove empty?))
(fn
;init (called at the start)
([] nil)
;reducing...
([accum item] (str accum " " item))
;completing (called at the end)
([ret]
(if (string? ret)
(clojure.string/trim ret)
ret)))
["qwe" "" nil "asd"])
Awesome, thanks. I will try to understand it. Nice to have a concrete example of transducers for something I am doing.
It’s a bit more verbose, but in my opinion it gives you a bit more flexibility once you need to do a more involved transformation (+ better performance for large collections)
Reduce using str
is bad, lots of allocation, lots of copying (not my bugaboo, but @ghadi will come for you)
If I have a vector in a text file, is it possible to read that file without converting the vector into one long string? or an easy way to strip the string away and just have the vector?
I do get
class java.io.BufferedInputStream cannot be cast to class
java.io.PushbackReader
when trying this.Hmm, I got it with
(read (
some times I spend way too much time code-golfing some code... anyway, I was wondering if someone can think of a more succinct way of doing this:
(map-indexed (fn [idx itm] (or itm ([:a :b :c] idx))) [1 nil 3]) => [1 :b 3]
that is, I want to provide default to values on a vector that may be nil
hmmm problem is that I have a different default per index
another example:
(keep-indexed #(or %2 ([:a :b :c] %1)) [1 nil 3])
(yeah I'm not putting that on my code hahah)
actually the keep-indexed is not any better than map-indexed in that example
so it makes a bit more sense here's the actual function I'm working with
(defn path-to-topic
"Converts a request path to a topic."
[path]
(let [[base name ext] (fu/get-base-name-and-ext path)]
[(str (or base "/") (or name "index")) (or ext "html")]))
so I have 3 defaults: one for the basename, one for the name, and one for the extension
Yea… if your requirement is to replace based on index, your original example might not be a bad option; you do need map-indexed to keep track of the index
gotcha
It encapsulates pretty well what you trying to achieve with the map-indexed and the (or …)
Do you control the data? Aka the vector with the nils? One solution to the problem is to avoid having the problem in the first place
As in, if you can manage to put the correct value instead of the nil to start with, you wouldn’t have to do this transformation
(map #(if (some? %1) %1 %2) [1 nil false] [:a :b :c])
As this doesn't work for false
values:
(map #(or %1 %2) [1 nil false] [:a :b :c])
I have a triply nested for-loop:
(for [[xc xp] xs]
(for [infile infiles]
(for [outfile outfiles]
[[xc infile] [xp outfile]]))))
What is the preferred way to remove the nesting from the results?
I do not want to use flatten, as then
[[xc infile] [xp outfiles]]
would also be flattened. But using 3X apply concat feels wrong :/.for
can take many collections so you could just have
(for [[xc xp] xs
infile infiles
outfile outfiles]
[[xc infile] [xp outfile]])
which may not need to do any un-nesting?Yase!!!1
btw - pedantic side note, for
is not a loop, it's a generator / list comprehension. loop
is our loop
Thanks for reminding me of the distinction 🙂
I'd skip it in any channel other than #beginners tbh :D
Why doesn't the REPL recognize the names of defrecords but understands the names of everything else in an imported module? At the prompt, I type (use 'lib-name.file-name :reload-all). Then the REPL tab-completes other names in the file, but not this one: (defrecord SomeRecord [^String name1 ^String name2])
repl doesn’t provide autocompletion functionality out of the box. it probably comes with your editor
which one you are using?
I'm using the REPL in a Mac terminal. Editor is Vim, but I'm not using that thing that puts the REPL at the bottom of the Vim window.
Autocompletion isn't really the point though. (Although I can see how I might have given that impression.) The problem is that even when I type (SomeRecord. v1 v2) I get an error that Clojure does not recognize that name.
(Class. arg)
is not a function, it's a class constructor invocation (a shorthand for calling new
). use
does not import classes. Use ->Class
instead (which is auto-generated by defrecord / deftype) - it's a function
also, use
is generally frowned upon these days
(it's handy sometimes in a repl of course)
more generally, though deftype
and defrecord
create classes and implement methods, they also provide function versions of all the many class operations, and the functions should be preferred over interop (though both functions and interop will access the same functionality)
Thank you guys!
How do I test contains?
for multiple keys?
I want a simpler (and more dynamic) way to write this:
(and (contains? mapped :username) (contains? mapped :password))))
But I want the 'name' and 'password' to be a given vector like ['name' 'password']
And I can't wrap my head around how to reduce this in a dynamic way
I typically use
I misread
=
and select-keys
for this(= keys-wanted (set (keys (select-keys m keys-wanted))))
and if username and password can't be nil, you can just use (every? mapped [:username :password])
wo okay. Thanks for the answers! Now I gotta sit with the Clojure docs and decypher all of them 😜 I've only been working in Clojure for ~2 weeks so getting used to it. Thanks!
is there a std function to assoc
in a key value pair only if the key is not already present?
not in core, but it's a one-liner
(fn [m k v] (if (contains? m k) m (assoc m k v))
or, if you also want to replace nils: (fn [m k v] (update m k (fnil identity v)))
Sounds good. I was heading that route but was curious. I'm constantly browsing the cheatsheet but always seem to stumble on a new, super useful core fn
(ins)user=> (defn impose [m k v] (update m k (fnil identity v)))
#'user/impose
(ins)user=> (impose {:a 0} :a 1)
{:a 0}
(ins)user=> (impose {:a nil} :a 1)
{:a 1}
(cmd)user=> (impose {} :a 1)
{:a 1}
though that function is small enough, I'd usually just call update with fnil identity instead of using the function in practice
small enough, and to me unsurprising enough
the tl;dr version is it returns the function with an added nil-check / default
(ins)user=> ((fnil + 1 2) 2 2)
4
(ins)user=> ((fnil + 1 2) nil 2)
3
(ins)user=> ((fnil + 1 2) 2 nil)
4
(ins)user=> ((fnil + 1 2) nil nil)
3
oh! - that's actually the common / simple solution and I somehow forgot it momentarily
(merge {:a 1} m)
- does exactly what was asked for, for any number of k/v pairs
And there is no way I didn't read about that behavior with merge
before either so that's on me too.
this happens a lot - clojure emulates math (set theory in particular) in ways that mean you find simple (once you learn them) but not always intuitive solutions to things
When you use reify to associate an object with a protocol, how do you then call a method on that object? For instance, if I have (defprotocol Whatever (method1 [ ] ...)
(method 2 [ ] ...))
Then define a function: (defn f [x] (reify Whatever (method1 [ ] ...) ;; method body here (method2 [ ] ...)) And then create an instance of the protocol (and I'm probably butchering the proper way to say that) (def uut (f ... )) How do I then call method1 on uut?
(method1 uut)
- but remember that method1
belongs to the namespace that defined the protocol, not the namespace that defined uut
Thank you!
protocol methods are first class and belong to the namespace where they are defined (like any other function you define into a var)
it's valid to use interop syntax also, but that should be reserved for the cases where it's needed
-
isn't a syntax, it's literally the function that subtracts
But at the end?
It might look better/be more understandable in context
right, or it's just bad code (using % and %2 in the same block make me think that it's just bad code)
bad as in poorly written, even if it works correctly
This was someone's solution for a 4clojure problem: http://www.4clojure.com/problem/134
(true? (__ :a {:a nil :b 2}))
(false? (__ :b {:a nil :b 2}))
(false? (__ :c {:a nil :b 2}))
oh yeah, some people treat 4clojure as a code golf (win by having the lowest character count total)
the second arg to a map is treated as the default value to return - hmm, working this out
for code golf purposes, I think it would work with any non nil 1 character value
yup, that would be it
odd choice to use -
instead of eg. 0
haha they could lose a character if they used nil?
or two if they used not
#(not(%2 % -))
though #(not(%2 % %))
is more aesthetically appealing somehow, and also works
it depends on if the function is supposed to specifically check for nil
or any falsey value
the test cases were already shared
I haven’t done 4clojure, so I wasn’t sure if there was descriptive text with hidden tests
aha - sometimes there are certain forbidden functions
and that
but the page was linked, and my #(not(%2 % %))
passes
in fact, #(not(%2% %))
passes, but yuck!
I'm not saying it makes the function coherent, just saying how it's interpreted by the compiler
I'm confused on how to switch to a different namespace in the repl. I have a project practice
so lein repl
throws me in practice.core=>
but if I try to switch to practice.foo
using (in-ns practice.foo)
I get an unable to resolve symbol error.
@chase-lambert You need to require
a namespace before you use in-ns
-- otherwise you won't get any Clojure symbols referred in.
also you need to quote the arg to in-ns (in-ns 'practice.foo)
- if practice.foo didn't exist, it wouldn't be an unresolved symbol error, it would just place you in a broken ns
(doto 'my.namespace (require) (in-ns))
is a safe way to enter a namespace -- @noisesmith I think I saw you recommend that recently?
haha, it's my goto
yeah, I picked it up from technomancy
Yeah, I know, but I've kind of gotten into the habit of using them around functions (but not around keywords) to make the intent clearer in my mind 🙂
Awesome. Any gotchas I should know when using things like (read-line
in the repl? I was going crazy using lein run
to run through such things. If I change a function in practice.foo
and save it, how do I make sure that change is reflected in my repl when calling that function again
my version of the snippet above is actually (doto 'my.ns (require :reload) in-ns)
often with a test invocation thrown in there too
I'm getting tripped up by a lazy sequence. In my REPL, (map csv/output-for-import applications)
gives me a bunch of strings, which is what I want. But I need those strings to be put into a file, and (spit "applications.csv" (map csv/output-for-import applications))
creates a file which reads solely "clojure.lang.LazySeq@95d817b⏎"
I tried sticking a doall
in there but not the right place, and it was just superstition
you want to call pr-str on the map
what is happening is calling str
on a lazy-seq gives you a useless output, pr-str
gives you the same representation you'd see in a repl
(ins)user=> (str (map inc (range 10)))
"clojure.lang.LazySeq@c5d38b66"
(ins)user=> (pr-str (map inc (range 10)))
"(1 2 3 4 5 6 7 8 9 10)"
general rule - if str
gives you nonsense, check if pr-str
is nicer (it often is, and you can extend it to new types too)
I'm dividing (/ 2158 3600) and printing the result. But I get 1079/1800 as the print and not the decimal
it's called a rational, it's used because it's accurate
you can force a floating point result by using at least one floating point argument
(cmd)user=> (-> 1.0 (+ 10000000000000001) (/ 10) (* 10) (- 10000000000000001))
0.0
(cmd)user=> (-> 1 (+ 10000000000000001) (/ 10) (* 10) (- 10000000000000001))
1N
you want format (in clj) or goog.format (cljs)
unless you mean decimal rounding of the number itself?
the typical trick is #(/ (Math/floor (* % 100)) 100)
- I don't think there's anything built in
you'd change 100
depending on how many digits you want
last I checked cljs didn't have format, and used goog.format instead
I thought you wanted to truncate the printed representation at first
that's a printing question - 1.10 is 1.1
so of course, that's my initial answer, format or goog.format
I guessed (http://good.fo/format "%.2f" my-num) but that didn't work
I should have double checked
(cmd)cljs.user=> (require 'goog.string)
nil
(cmd)cljs.user=> (goog.string/format "%.2f" 1.111)
"1.11"
if you use tap>
instead of prn
you can do (add-tap prn)
to start printing, and (remove-tap prn)
to make it stop
though tap> only takes a single arg, so you might need to wrap the args in [] or whatever
you could use a global substitution to turn all (prn
into #_(prn
now that I think about it