This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-02-19
Channels
- # announcements (1)
- # aws (9)
- # beginners (136)
- # boot (4)
- # cider (11)
- # clara (36)
- # clojure (337)
- # clojure-europe (5)
- # clojure-italy (3)
- # clojure-nl (2)
- # clojure-spec (9)
- # clojure-uk (112)
- # clojured (7)
- # clojurescript (93)
- # core-async (2)
- # cursive (30)
- # datomic (6)
- # duct (4)
- # emacs (2)
- # figwheel (1)
- # figwheel-main (7)
- # fulcro (153)
- # kaocha (1)
- # off-topic (12)
- # om-next (1)
- # pedestal (58)
- # planck (6)
- # re-frame (15)
- # reitit (11)
- # shadow-cljs (113)
- # spacemacs (1)
- # specter (3)
- # vim (8)
Just trying to write a function that returns distinct elements from a vector . Is it ok to use the 'initial value' in reduce like this ? (defn accumulate-dups [result item] (let [ map-unq (first result) vec-unq (second result)] (if-not (contains? map-unq item) [(assoc map-unq item 0) (conj vec-unq item)] result))) (second (reduce accumulate-dups [{} []] [1 10 9 9 2 4 4 7 8] ))
@UEHMNPFC5 Your code looks workable, but I can see a couple improvements you could make. First of all, you can use destructuring to simplify your handling of the results
, like this:
(defn accumulate-dups [result item]
(let [[map-unq vec-unq] result]
...
This will automatically separate out the items that are inside the result for you without having to write out first
and second
.
Next, you are using a map to check for values you’ve already seen, which is how I would do it if I were using a language that didn’t have sets. Clojure, however, does have sets, so that’s what I’d use here:
(defn accumulate-dups [result item]
(let [[set-unq vec-unq] result] ;; `set-unq` will be a set instead of a map
(if-not (contains? set-unq item)
[(conj set-unq item) (conj vec-unq item)]
result)))
and you’d call it like (second (reduce accumulate-dups [#{} []] [1 10 9 9 2 4 4 7 8]))
(notice #{}
instead of {}
in your initializer)
The cool thing about sets is that you can use them as functions, to test if an item is a member of a set, so you can do this:
(defn accumulate-dups [result item]
(let [[set-unq vec-unq] result]
(if-not (set-unq item) ;; set-unc is a set, so it automatically tests for `contains?`
[(conj set-unq item) (conj vec-unq item)]
result)))
It’s also very easy to turn a vector into a set on the fly, so you don’t need to carry the map-unq
value around inside your result. Instead you can just write this:
(defn accumulate-dups [result item]
(let [seen? (set result)]
(if-not (seen? item)
(conj result item)
result)))
and call it like this: (reduce accumulate-dups [] [1 10 9 9 2 4 4 7 8])
A set when called with an arg returns the arg if the set contains it, otherwise returns nil. So it acts as a predicate that checks membership only for truthy members. (map #(if (#{1 nil false} %) "yes" "no") [2 1 nil false])
Bit of a gotcha here.
Yeah, I forgot to mention that, thanks for pointing that out (especially since this is for #beginners)
thanks so much! @manutter51 for walking me through this. I particularly like the way the set is constructed on the fly. So do you decide on whether carrying the map around / create a new set on every invocation of accumulate-deps depending upon the size of the input vector or you just write the crisp code straight away . thanks @UBU6QCSJH for pointing out the gotcha.
I would usually use (set result)
every time around, unless/until the performance started to suffer. It’s fairly easy to switch back to passing the seen?
set back in as an argument if you need to later on, so I’d start with the simpler version by default.
Thanks!!
In reality though, a lot of these things have high quality implementations in clojure.core
, so you don't need to write them yourself (like distinct
). Clojure gives you a far larger vocabulary than the typical map, filter and reduce most languages that claim to support functional programming give you.
the order of elements in the vector need to be maintained in the result
I do this to get time in millisecond. :date value is an object of java.util.Date (.getTime (:date myobj)) What is the idiomatic way of getting zero if the date obj is null?
ohh! Beautiful. tks!
I have to access the inner cropper object from the ready event object. I am using cropperjs. The event object is as in the below image. But when I try to get (.-target event), I am getting HTML tag as <img src="https://static.pexels.com/photos/248797/pexels-photo-248797.jpeg" class="spots_ui_components_cropper--cropped-image27970 spots_ui_components_cropper--cropped-image cropper-hidden">
Any idea?
as shown in the readme, you should use (js-this)
inside your ready
fn to get it
new Cropper(image, {
ready() {
// this.cropper[method](argument1, , argument2, ..., argumentN);
this.cropper.move(1, -1);
// Allows chain composition
this.cropper.move(1, -1).rotate(45).scale(1, -1);
},
});
I tried js-this, this-as etc. But, they all are returning the HTML tag.
(js/Cropper. image
#js{:ready (fn []
(let [cropper (.-cropper (js-this))]
(etcetera)))})
what are some commons reasons for a try-catch's catch block not being triggered and propagating up to the calling function's catch function?
for me, it's usually that my code has a bug :)
best to validate exception actually being thrown and with the type you expect
(I'm assuming clj, but there may be gotchas I'm unfamiliar with in cljs)
Okay so a try-catch's catch function will always fire. Is there a catch-all exception I can use? I thought (catch Exception e . . . )
was the catch all
Throwable is the top of the hierarchy
(def list-names '("Tom" "Harry" "Richard"))
(defn throw-func
[nme]
(str "hello " nme)
(throw (Exception. "I just threw you an error")))
(defn tc-test
[]
(let [futures (map (fn [name]
(let [sample (fn [name]
(try
(println "Sanity Check")
(throw-func name)
(catch Exception e
(println "Got an error here!"))))]
(future (sample name))))
list-names)
response (doall (map deref futures))]
1))
(defn foo
[]
(try
(tc-test)
(catch Exception e
(println "Handling error here as well"))))
I have this bit of code... Not exactly this but the structure is close enough. In my repl this works as expected and I am seeing "Got an error here!" but when running the app version of the example I see "Handling error here as well".futures are in a different thread with a different stack so you'll catch it at Got an error, and never in the one in foo
(Another thing that’s tripped me up — though doesn’t seem to be tripping you up — is thinking I was going to catch an Exception
but an Error
was thrown, or vice versa. In that case I often used Throwable
because the distinction didn’t matter for my use case.)
deref on a future with an uncaught exception will rethrow at the point of deref, but there's no path in that code for that which I can see
yeah, you'd usually want (run! deref (doall futures))
- unless your real code uses the value of response
@futuro Not following... @noisesmith I see what you are saying but in my repl the catch in the foo function does not run. Wouldn't the exception propagate up to that functions try-catch?
@mario.cordova.862 regarding what futuro was saying there are multiple types that can be thrown, Exception is one of them, but they are all subtypes of Throwable
in particular, Error is not an Exception
it's regarding the usage of the word "error" in describing your code that doesn't catch Error
they may not be what you expect, and they generally tell you exactly where they come from
(ins)user=> (try (assert false) (catch Exception _ :OK))
AssertionError Assert failed: false user/eval149 (NO_SOURCE_FILE:3)
(ins)user=> (try (assert false) (catch Error _ :OK))
:OK
@noisesmith he is saying that the handler in foo actual runs
if j.u.c.ExcecutionException is the type of the exception something else entirely is happening, yeah...
to be clear, what it appears to be is not an exception from the body of the future (where the try catch is) but an exception from the machinery that executes futures
setting the default uncaught exception handler can help detect weirdnesses like this
#error {
:cause Error getting number of borrowers
:via
[{:type java.util.concurrent.ExecutionException
:message java.lang.Exception: Error getting number of borrowers
:at [java.util.concurrent.FutureTask report FutureTask.java 122]}
{:type java.lang.Exception
:message Error getting number of borrowers
:at [com.some.project$get_number_of_borrowers invokeStatic file-name.clj 937]}]
:trace
[[com.some.project$get_number_of_borrowers invokeStatic file-name.clj 937]
[com.some.project$get_number_of_borrowers invoke file-name.clj 933]
[com.some.project$add_fields$fn__21224$lookup_call__21225$fn__21226 invoke file-name.clj 1255]
[new_reliquary.core$wrap_with_named_transaction$fn__20964 invoke core.clj 28]
[new_reliquary.core.NewRelicTracer trace core.clj 32]
[new_reliquary.core.NewRelicTracer doTransaction core.clj 35]
[new_reliquary.core$with_newrelic_transaction invokeStatic core.clj 45]
[new_reliquary.core$with_newrelic_transaction invoke core.clj 43]
[com.some.project$add_fields$fn__21224$fn__21229 invoke file-name.clj 1260]
[clojure.core$binding_conveyor_fn$fn__4676 invoke core.clj 1938]
[clojure.lang.AFn call AFn.java 18]
[java.util.concurrent.FutureTask run FutureTask.java 266]
[java.util.concurrent.ThreadPoolExecutor runWorker ThreadPoolExecutor.java 1149]
[java.util.concurrent.ThreadPoolExecutor$Worker run ThreadPoolExecutor.java 624]
[java.lang.Thread run Thread.java 748]]}
are you doing it inside somethig lazy?
the throw - laziness can make exceptions escape a try/catch block
it is wrapped in a java.util.concurrent.ExecutionException which happens when derefing the future
yeah, and the exception won't happen until that map is realized, which in some cases can make a throw "escape" from a try/catch body
simple version:
(user=> (future (try (map / [1 0]) (catch Exception _ :OK)))
#object[clojure.core$future_call$reify__8097 0x7a560583 {:status :pending, :val nil}]
user=> (deref *1)
ArithmeticException Divide by zero clojure.lang.Numbers.divide (Numbers.java:163)
without the laziness
user=> (future (try (mapv / [1 0]) (catch Exception _ :OK)))
#object[clojure.core$future_call$reify__8097 0x1dd6d4b7 {:status :pending, :val nil}]
user=> (deref *1)
:OK
Hello everyone! I am quite new to Clojure and I have a couple of questions regarding best practices / fp mindset:
So I have a list of jobs (a queue if you will), and I am implementing a dequeue function. So the function receives the queue and the criteria to choose which job will be processed. Then, the function returns the queue without the job choosen, and the job choosen separated. Given that I have to return 2 separated things, what is the best approach to do this?
- Return a vector with them like [queue job]
? (this one I find odd, as the order will matter when handling the return).
- Return a hash map like {:queue queue :job job}
. This one sounds ok for me given that I come from Node.js background.
- Or should I do this on two separated functions? One to find the job with the criteria (returning the job) and a second one to remove a job with the criteria (returning the updated queue), much like peek
and pop
?
@noisesmith I think you are right. There is a lot of laziness going on here and I believe its causing the exception to escape
yeah, using anything lazy requires a lot of care around "block" oriented code like try/catch
or with-open
etc.
Thanks for the help y'all @noisesmith @hiredman @alexmiller @futuro
@leonardo.freitas.s I would say either the vector or the hash map, whichever you're more comfortable with. I frequently use a vector when I need to return 2 values, because it's pretty easy to use destructuring to get the values out (let [[a b] (some-fn-that-returns-2-items-in-a-vector)] ...)
More than two, I'd probably go with a hash map.
@leonardo.freitas.s There's no wrong or right, or established best practice here.
@leonardo.freitas.s the two functions solution is just asking for race condition errors
Awesome, I guess I will go with the hash map, makes a lot of sense for me. Thanks @didibus, @noisesmith and @manutter51!
@leonardo.freitas.s by the way - what mutable container do you plan on using? or is it a purely functional version built on recur or something?
Purely functional for now. Just a simple job queue that should be able to find the most adequate jobs to the given criteria
in that case, you probably don't have to worry about race conditions at all as the natural way to implement that would be strictly single threaded
Yeah, but two functions would also mean iterating through the queue twice
So I guess hash map / vector is the best way here
if your comparison function is a valid sorting comparator, you can use sorted-set-by
and assume the first item in that ordered set is always highest priority
Hmm, not sorting it as priority changes with every criteria. But well, tyvm for the help!
no, because you still need to reconstruct the vector
using a set allows efficiently removing random items though (at the cost of no fixed order - sounds like you wouldn't want fixed order here anyway)
a set would eliminate duplicates though, so you'd need to ensure that things that are distinct contained some distinct key at least
O(n) for the n args after the coll
That seems like, I don't know all the details of the vector, but it seems like if you can subvec in O(1), you should be able to concat in O(1) no?
the tree structure of a vector means it's not that simple, AFAIK clojure vectors are not optimized for fast concat
the cost of adding N items scales linearly with the size of N
at least it doesn't also scale with the size of the vector too
or something like finger-tree that's meant for this use case
or set, as long as you ensure entries that should be unique are, and stable ordering isn't needed
So called pseudo constant. Not sure if Finger Tree are similarly pseudo constant log n
so I'm reading about for
and it seems like it will be super powerful and useful but it is confusing me. This example:
(def list-of-lists [[1 2 3] [4 5 6] [7 8] [9 10] []])
(for [l list-of-lists
x l]
x)
;; => (1 2 3 4 5 6 7 8 9 10)
Tbh, I don't know how it's getting to that. I think I'm picturing those bindings like a let
binding but then I feel like I should just get the original list-of-lists
back.each binding in for's vector is an inner loop on its right hand side
maybe this helps
user=> (for [x [1 2 3] y [4 5 6]] [x y])
([1 4] [1 5] [1 6] [2 4] [2 5] [2 6] [3 4] [3 5] [3 6])
the literal translation of your example is "for each l in list-of-lists, and each x in each l, x"
that example makes much more sense to me. I think I almost have an understanding of the one I posted. Let me pretend I'm in a hammock for a little bit.
then there's this minor alteration to my previous example
user=> (for [x [1 2 3] y [4 5 6] z [x y]] z)
(1 4 1 5 1 6 2 4 2 5 2 6 3 4 3 5 3 6)
I think what's getting me is "for each l in list-of lists" "l" is each inner vector? How is [l list-of-lists] telling it that?
yes, except special things like :when
:while
and :let
yeah - also doseq is nearly identical except its body is an implicit do and it always returns nil and isn't lazy
mapcat would see a lot more action if we didn't have for