Fork me on GitHub
#beginners
<
2021-09-29
>
Redbeardy McGee01:09:22

I'm learning clojure, and found a couple of sites with exercises supporting clojure. I feel like an idiot for spending a day digging through clj docs trying to find the most applicable core language features for the very first exercise, but I am definitely missing something. The problem statement: Write a program that prints the temperature closest to 0 among input data. If two numbers are equally close to zero, positive integer has to be considered closest to zero (for instance, if the temperatures are -5 and 5, then display 5). Display 0 (zero) if no temperatures are provided. I can get the value closest to zero this way: `(reduce min (map #(Math/abs %) temperatures))` I can probably use `(if (seq temperatures) (min ...) 0)` to guard against an empty seq. However, this approach loses all negative values, which results in returning a positive integer even when all inputs are below zero. Does this mean I've tried to approach the problem incorrectly, or am I missing a functional idiom that could preserve the negative sign?

Duane Bester01:09:08

just messing around:

``````(def temps [-7 5 4 -8 9 -3 2])
(if (empty? temps) 0
(last (first (sort (zipmap (map (partial Math/abs) temps) temps)))))``````

Duane Bester01:09:42

made the abs vals the keys of a map & the values the temps, then sort the map, then grab first key,val pair, then the val

hiredman01:09:07

Math/abs being a java method is not something you can call partial on, you must using clojurescript?

hiredman01:09:28

The correct solution is very obviously a fold

Redbeardy McGee01:09:42

That's an interesting direction to go in. Thanks for the neat idea.

hiredman01:09:01

`(reduce min ...)` Is almost perfect

hiredman01:09:30

But min is operating with the default ordering of numbers

Redbeardy McGee01:09:53

I'm trying to "think functionally" for the solution which is why I arrived at the reduce, but I got stuck after that.

hiredman01:09:14

`(reduce my-min identity-value ...)`

hiredman01:09:20

Is what you want

hiredman01:09:50

Where my-min and identity value are monoid over your values

Redbeardy McGee01:09:16

I haven't grokked monoid yet

hiredman01:09:30

The link about comparators should be good fodder for writing my-min if not exactly how to do that

hiredman01:09:46

A monoid is not a monad to be clear

Redbeardy McGee01:09:00

It's okay, I don't understand either yet 🙂

hiredman01:09:40

A monoid is a function that is really useful with reduce

hiredman01:09:29

+ has an identity 0 (x+0=x)

hiredman01:09:23

So you can write `(reduce + 0 [1 2 3])` and it will work

Redbeardy McGee01:09:03

hiredman01:09:04

For min, the identity value is something like positive infinity, I often use Long/MAX_VALUE

hiredman01:09:07

The min of x and Long/MAX_VALUE is in practice often x

Redbeardy McGee01:09:08

Right, of course

Duane Bester01:09:23

cljs version

``(if (empty? temps) 0 (last (first (sort (zipmap (map #(.abs js/Math %) temps) temps)))))``

hiredman01:09:37

With a complexity of n^4 or something

hiredman01:09:54

Where a fold will do it in n

hiredman01:09:47

(sorting is n^2, zipmap is n, last is n)

Duane Bester02:09:55

yeah the java sort is O(n log n). agreed there is a more efficient approach

Redbeardy McGee05:09:55

finally sat down to finish this and find out that this platform has broken boilerplate code throwing a NumberFormatException by trying to call `Integer/parseInt` on the empty string.

Redbeardy McGee01:09:57

Sorry, I don't understand what the implication of this link is meant to be.

hiredman01:09:09

You are comparing numbers with certain rules, the tool for comparing things are comparators, which is what that link is about. If you have a comparator that uses the order defined by your rules, then you can use it lots of other things

hiredman01:09:38

sort, sorted-set, etc as mentioned in that link

sova-soars-the-sora05:09:13

comparators ... i feel like that's a deeper java thing, maybe a few clear examples of wtf reduce is doing would be better for a beginner sure, you'll eventually want to make your own comparator but what am reduce? reduce is a magic fairy with 2 hands that starts with an initial value, does some operation on initial value and first value, and then stores the result as the new initial value, and "collapses" a collection two elements at a time until it's reduced to one value

Redbeardy McGee06:09:06

Thanks, I get what reduce does, though I struggle to know when it's appropriate to apply

raspasov08:09:13

`reduce` is appropriate whenever you need to “visit” every element of a collection in order to obtain a result. In practice, I don’t usually use it very often. Depends on the type of problems you’re solving. Often, there can be a simpler way to solve a problem via `map` and/or `filter` if you’re processing items one by one. But if you really need to check every item, `reduce` is your friend.

raspasov08:09:01

If you need to go through every item of a collection in order to produce a side effect (send items to analytics, save to a database, etc), there’s `run!`

raspasov08:09:52

Which is basically `reduce` 🙂

``````(defn run!
"Runs the supplied procedure (via reduce), for purposes of side
effects, on successive items in the collection. Returns nil"
[proc coll]
(reduce #(proc %2) nil coll)
nil)``````

4
😄 4
Redbeardy McGee06:09:17

seemed perfect for this case

Benjamin08:09:22

``````(defn assoc-email [m]
(assoc m :email (fetch-email (:user-id m))))``````
what would be an unsurprising name for this `aasoc-email` function?

delaguardo08:09:48

`ensure-email` ?

👍 1
pavlosmelissinos09:09:48

My 2 cents: I probably wouldn't put it in a function on its own. Assuming that there are other keys with trivial logic like this that you want to assoc to `m`, I'd rather gather them all into an `enrich-m` function. Also what does `m` represent? Something more specific, like `user` would communicate intent better.

Johan Thorén09:09:47

@UEQPKG7HQ, I used to think that `user` would be a better name, but the way I interpret https://guide.clojure.style/#idiomatic-names, it seems `m` would be preferred?

pavlosmelissinos09:09:32

@U02DW53HCSE I don't think that the style guide suggests that it's idiomatic to name every map `m`. `m` implies an arbitrary map. If you're writing a function that operates on generic data structures, it's idiomatic, sure. But in this particular example, `m` represents an entity that has an email (from the context it seems like it's a user but I can't be sure). And yes, you can't tell if `user` is supposed to be a map or a collection or something else but that's what spec is for.

didibus11:09:58

You should try to write your code in a pure fashion. Do this instead:

``````(defn assoc-email [m email]
(assoc m :email email))
``````
Now the name you have makes sense. And in a higher level function you'd now coordinate the two:
``````(->> (:user-id user)
(fetch-email db)
(assoc-email user))``````

👍 3
sova-soars-the-sora04:10:08

Argument order anyone? Could also use {:keys [m email]}

zugnush09:09:19

I'm really struggling with a java interop problem. I think because there are multiple implementations of an interface. I'm trying to use https://github.com/bbottema/rtf-to-html.

zugnush09:09:52

``````(ns zugnush.rtf2html
(:gen-class)
(:import
[org.bbottema.rtftohtml.impl RTF2HTMLConverterJEditorPane]
[org.bbottema.rtftohtml RTF2HTMLConverter]))

(.rtf2html (RTF2HTMLConverterJEditorPane.) "xx")
(.rtf2html (RTF2HTMLConverter.) "xx")
;; both no matching ctor

(RTF2HTMLConverter/rtf2html "xx")
(RTF2HTMLConverterJEditorPane/rtf2html "xx")
;; both no matching method``````

dpsutton10:09:57

there are no public constructors, just a public static instance for you to use https://github.com/bbottema/rtf-to-html/blob/master/src/main/java/org/bbottema/rtftohtml/impl/RTF2HTMLConverterJEditorPane.java#L20-L22. It should be something like `(.rtf2html RTF2HTMLConverterJEditorPane/INSTANCE "the rich text")`

dpsutton10:09:34

``````RTF2HTMLConverter converter = RTF2HTMLConverterJEditorPane.INSTANCE;
RTF2HTMLConverter converter = RTF2HTMLConverterClassic.INSTANCE;
RTF2HTMLConverter converter = RTF2HTMLConverterRFCCompliant.INSTANCE;

String html = converter.rtf2html("RTF text");``````

zugnush10:09:55

Thanks. My java's not good.

Akiz12:09:07

I am coming from the same place - nearly no Java experience. I recommend you to find some really basic Java tutorial and try to do it in Clojure. It will help you to get basic Clojure / java interop really quick.

Lycheese11:09:41

Is there a way for an anonymous function to reference itself to make multiarity nicer? (I'm trying to have an overloaded re-frame event that allows optional arguments. I'm currently doing this with a rest argument and just fishing out the relevant information from a map I supply there, but if possible I'd like to make it less janky.) e.g. what would ? be here? `(fn ([some opts] (? some opts default-parameter))` `([some opts optional-argument] (…)))`

delaguardo11:09:51

anonymouse functions can have a name `(fn foo [] …)` and this name resolvable within functions’s body

``````(fn foo
([] (foo nil))
([arg] ...))``````

dpsutton11:09:22

anonymous functions by definition do not have names 🙂 . but you can name a function expression

Lycheese11:09:22

Ah I didn't know that. Thank you very much both of you :)

dpsutton11:09:52

``````(let [f (fn give-it-a-name
([] (give-it-a-name :foo))
([arg] arg))]
(f))
:foo``````

Benjamin17:09:44

is it fine to create a core.async `go` routine that ends up being blocked by a parking take forever? Or should these be closed?

``````(a/go-loop
[]
(let [thing (a/<! c)]
...)
(recur))``````
and `c` at some point just doesn't deliver anything anymore

if you know that input is done, that channel's producer should close it

Benjamin17:09:11

I see. I guess it is a common pattern to have a coll, (say a list of users) and fetching for each element something, in parallel. So I get a list of say emails or sth. Is this what `pipeline` is for?

generally yes - you can look at `pipeline-async`

but it's not tying up a thread while waiting

Noah Bogart19:09:34

in the clojure core function `select-keys` , what does `(. clojure.lang.RT (find map (first keys)))` do?

Noah Bogart19:09:56

or more specifically, what does the `(. clojure.lang.RT` part do? the rest looks like a normal function call

Apple19:09:14

java interop

Apple19:09:24

runtime probably

Apple19:09:36

``(. clojure.lang.RT (find map (first keys)))``
can be just
``(find map (first keys))``
cause i see in source code that `find` is now defined as a clj func.

Noah Bogart19:09:30

right, that’s part of what’s confused me. i’m not sure what it’s doing in the algorithm

Apple19:09:11

source code is there if you want to look it up the algo.

Noah Bogart19:09:24

same with the find definition, why the call to `clojure.lang.RT`?

Apple19:09:59

`find` was probably added later.

Noah Bogart19:09:43

ah, excellent. thanks for finding that

noisesmith16:09:05

also the idiomatic interop syntax would usually be:

``(clojure.lang.RT/find map (first keys))``

👍 1
noisesmith16:09:25

(that expands to the `(. class static-method ...)` version used here)

Alex Miller (Clojure team)19:09:03

It is just a function call to the RT class, which is the internal Clojure runtime

👍 1
Noah Bogart19:09:55

if I’m implementing my own version of `select-keys` that will skip nil values, do I need it? i would have written the line without that part

George Silva20:09:18

Hello dear friends! I have been doing https://exercism.org/tracks/clojure/exercises/elyses-destructured-enchantments exercise on exercism. The last exercise on destructuring made me sweat a bit. It is passing, but I was wondering if there is a way to be less verbose on `insert-face-cards`. The tricky part is a test that checked if we were return nil as the first element, when passed an empty vector to `insert-face-cards`. I came up with the filter, but still a bit puzzled.

``````(ns elyses-destructured-enchantments)

(defn first-card [deck]
(let [[top-card] deck]
top-card))

(defn second-card
"Returns the second card from deck."
[deck]
(let [[top sec] deck]
sec))

(defn swap-top-two-cards
"Returns the deck with first two items reversed."
[deck]
(let [[a b & rest] deck]
(concat [b a] rest)))

"Returns a vector containing the first card and
a vector of the remaining cards in the deck."
[deck]
(let [[a & rest] deck]
(vector a rest)))

(def face-cards
["jack" "queen" "king"])

(defn insert-face-cards
"Returns the deck with face cards between its head and tail."
[deck]
(let [[a & others] deck]
(filter some? (into [a] (concat face-cards others)))))``````

bhenry20:09:55

you'll need the `filter` to lose the `nil`s but you don't need `into` since you're already using `concat`

``````user=> (concat [1] ["a" "b" "c"] [2 3 4])
(1 "a" "b" "c" 2 3 4)
user=> (concat '(1) '("a" "b" "c") '(2 3 4))
(1 "a" "b" "c" 2 3 4)``````

George Silva14:09:48

Thank you both for the suggestions! 🙂

Ed17:09:09

Is the `a` the only thing that's actually going to put a `nil` in the results of `insert-face-cards`? If so you might also be able to write that as `(concat (when a [a]) face-cards others)`

1