Fork me on GitHub
#beginners
<
2020-01-05
>
kamuela00:01:46

How do you use recur with the second version of a function? Ignore the logic behind this and whether or not it works, as I'm fighting with it at the moment, but currently getting a syntax issue:

(defn determine-digits
  ([remaining-digits]
   (if (= (quot remaining-digits 10) 0)
     1
     (recur (quot remaining-digits 10) 1)))
  ([remaining-digits digits-so-far]
   (if (= (quot remaining-digits 10) 0)
     digits-so-far
     (recur (quot remaining-digits 10) (inc digits-so-far)))))
Syntax error (IllegalArgumentException) compiling recur at (form-init4233438687175101551.clj:5:6).
Mismatched argument count to recur, expected: 1 args, got: 2
I'm guessing because on line 5 recur somehow only recurs to that particular arity instance of the function? What's a better approach for what I'm trying to do syntactically, again, glossing over other logic issues

didibus00:01:40

The recur expression must match the arity of the recursion point exactly.

didibus00:01:16

You're trying to recur into another function, even though it looks like the same function, you're creating two functions that differ by arity

didibus00:01:33

And you can't recur into the other one

didibus00:01:58

You need to merge both of them into the two arity one, and then make the one-arity a call to the two-arity

kamuela01:01:09

Ended up with

(defn digits
  ([remaining-digits]
   (digits remaining-digits 1))
  ([remaining-digits digits-so-far]
   (if (= (quot remaining-digits 10) 0)
     digits-so-far
     (recur (quot remaining-digits 10) (inc digits-so-far)))))

didibus02:01:05

Yes, that's how you do it

hindol.adhya03:01:08

You can alternatively use the loop/recur syntax. Typically, calls to the two arity function will be internal so you can avoid exposing it.

jaihindhreddy11:01:43

If you don't particularly care about performance, you could just define it like this ๐Ÿ˜›:

(defn digit-count [n]
  (count (str (Math/abs n))))

kamuela11:01:16

@ That's how I did it initially. This is for exercism. My mentor said to do it arithmetically. I wonder, truly though, how much more/less performant each version is

jaihindhreddy11:01:07

Well, here they are:

user=> (defn digits2 [n]
  #_=>   (loop [num (long n)
  #_=>          d 1]
  #_=>     (let [q (quot num 10)]
  #_=>       (if (zero? q)
  #_=>         d
  #_=>         (recur q (inc d))))))
#'user/digits2
user=> (defn digits1 [n]
  #_=>   (count (str (Math/abs (long n)))))
#'user/digits1
user=> (crit/quick-bench (digits1 1234567890123456789))
Evaluation count : 7296390 in 6 samples of 1216065 calls.
nil
             Execution time mean : 75.719094 ns
    Execution time std-deviation : 2.568614 ns
   Execution time lower quantile : 72.614107 ns ( 2.5%)
   Execution time upper quantile : 78.657657 ns (97.5%)
                   Overhead used : 9.051454 ns
user=> (crit/quick-bench (digits2 1234567890123456789))
Evaluation count : 15884838 in 6 samples of 2647473 calls.
nil
             Execution time mean : 28.911209 ns
    Execution time std-deviation : 0.276928 ns
   Execution time lower quantile : 28.680677 ns ( 2.5%)
   Execution time upper quantile : 29.357405 ns (97.5%)
                   Overhead used : 9.051454 ns

Found 1 outliers in 6 samples (16.6667 %)
	low-severe	 1 (16.6667 %)
 Variance from outliers : 13.8889 % Variance is moderately inflated by outliers
The long is to avoid reflective calls.

kamuela11:01:53

Wow, thank you for that. Super interesting, ok so now we know it's definitely slower the way I was doing it

iagwanderson00:01:06

any problem by changing this recur on line 5 to a call with the name of the function?

kamuela00:01:49

Hmm I somehow thought you shouldn't do that for reasons that escape me, no TCO I think it was

iagwanderson00:01:56

(defn determine-digits
  ([remaining-digits]
   (if (= (quot remaining-digits 10) 0)
     1
     (determine-digits (quot remaining-digits 10) 1)))
  ([remaining-digits digits-so-far]
   (if (= (quot remaining-digits 10) 0)
     digits-so-far
     (recur (quot remaining-digits 10) (inc digits-so-far)))))

kamuela00:01:25

You're right though, it'll only cause one stack frame deep

kamuela00:01:42

Something about that feels like it's not idiomatic though

iagwanderson00:01:11

when I work with multi-arity functions this show up a lot

iagwanderson00:01:28

and I've been doing this

iagwanderson00:01:04

quick query through a code base that I am using and I hit an example

(defn now
  ([]
   (now 0))
  ([delay]
   (let [now (java.time.Instant/now)]
     (java-time/plus now (java-time/days delay)))))

kamuela00:01:15

In my case though, I'm going to need the original call to look like (determine-digits 1123), so there is indeed a requirement for a single arity rather than a 0-arity

seancorfield00:01:10

It's common to have several arities that all end up calling one specific arity with default values filled in for the other arguments.

kamuela00:01:15

Oh yeah I didn't notice what you meant, you just meant without using recur

kamuela00:01:38

Ok if that's the pattern I'll try it

seancorfield00:01:23

@kamuela I think in your case you don't need to the if in the 1-arity. Just call the function passing 1 as the second arg.

seancorfield00:01:03

And just pass remaining-digits, without calling quot on it.

seancorfield00:01:58

(defn determine-digits
  ([remaining-digits]
   (determine-digits remaining-digits 1))
  ([remaining-digits digits-so-far]
   (if ...)))

kamuela00:01:26

Thanks, that's exactly what I ended up doing

kamuela00:01:05

Well since we've dove straight to the logic, here's a brain-teaser: is 0 a single-digit number?

kamuela00:01:13

Thanks @iagwanderson you've solved the syntax issue with that pattern

iagwanderson00:01:56

as we like in clojure, the definition from Merriam-Webster says that 0 is a single-digit number.

single-digit adjective

Definition of single-digit
: having a number or percentage that is 9 or less

kamuela00:01:24

Not really a satisfying definition, 8.72 is single digit as is -255

iagwanderson00:01:06

so we should improve the definition to find that out ๐Ÿ˜ƒ hehe

lennart.buit00:01:25

well are -9 .. -1 single digit or not?

kamuela00:01:49

Any real integer between -9 and 9 excluding 0 would be a precise definition, but I actually don't know what the real definition is

lennart.buit00:01:55

hehe, you are right, depends on your definition

lennart.buit00:01:14

Iโ€™d say that counting the digits in 0 yields 1 digit, so should also be included

glfinn8311:01:13

Is there something like Spring Data JPA for Clojure? There I can declare a repository (an interface) for a component (a POJO) and I get all the CRUD operations 'for free' (no code needed). It also makes it really easy to add custom queries.

vachichng11:01:33

maybe https://github.com/metabase/toucan is the closest thing to that.

glfinn8312:01:39

Yeah seems pretty close. Thanks for that!

iagwanderson11:01:24

I am not from Java-land, but I did a small research on Data JPA and until now I haven't found a framework web that does that in clojure. In fact, I never used any clojure "framework" to build my web apps

alper12:01:48

I had asked why there were three ways to create a record. Clojure Applied starts off explaining it. Also I created a channel for #clojure-applied.

alper12:01:33

Renamed to #books-clojure-applied

ewan.valentine8918:01:04

Hey! I'm very new to Clojure, I'm attempting to read some data from an API, but it's being returned as a clojure.lang.PersistentVector, is there some special way to access the values in that data type? I can't seem to figure it out? A snapshot of the data looks like this when I print it out:

[{away_l 4, home_gp 13, stage_id 12051081, away_w 5, recent_form WWLWD, country England, season 2019/2020, away_ga 18, overall_l 9, home_ga 16, overall_d 6, away_gs 19, overall_w 11, overall_ga 34, points 39, team_id 9221, home_gs 21, position 9, comp_group nil, home_w 6, home_l 5, team_name Hull, status same, comp_id 1205, away_d 4, overall_gp 26, overall_gs 40, home_d 2, away_gp 13, round 26, gd +6, description }]
I'm attempting to access it like:
(defn fetch-teams [id]
  (doseq [p (api->fetch-teams id)] (println (:team_name p))))
But :team_name is nil

iagwanderson18:01:23

maybe the keys of your map p are strings. try (get p "team_name")

ewan.valentine8918:01:26

Oh my god, that was absolutely correct!

iagwanderson18:01:29

if you need to transform these keys into keywords you can use the function clojure.walk/keywordize-keys

ewan.valentine8918:01:39

Thank you so much, it never occurred to me!

ewan.valentine8918:01:45

Ahaa that's really useful

iagwanderson18:01:55

what are you using to make the API call?

iagwanderson18:01:08

usually they have an option to automatically convert this for you too

ewan.valentine8918:01:33

Yes that's the one, I was using :as :json for the others, but because this was returning an array, it didn't work the same, I had to do :as json-string then had to use json/read-str

iagwanderson18:01:04

ahn.. json/read-str has an option for keywordize

iagwanderson18:01:22

(json/read-str <your-str> true)if I recall

ewan.valentine8918:01:50

:key-fn keyword did the trick ๐Ÿ™‚

jsyrjala19:01:09

Use :as :json-strict It will work better with arrays in latest clj-http

roguas19:01:04

Hi all ๐Ÿ™‚, I have a question I am struggling with let's say printing ranges, however including negative integers, where I know I could technically do (range 2 -3 -1) but it seems clunky, I am looking for a function that would return numbers within a range [2 -5] including range boundries

roguas20:01:08

Ok, so I have decided to reimplement range

(defn irange [start end]
  (let [direction-positive (if (< start end) true false)
        end-operator (if direction-positive > <)
        jump-operator (if direction-positive inc dec)]
    (loop [x start
           l []]
      (if (end-operator x end)
        l
        (recur (jump-operator x) (conj l x))))))

andy.fingerhut20:01:25

That is a reasonable-looking implementation. I only wanted to point out that if you call range with the appropriate parameters inside of irange you can take advantage of performance optimizations of range , e.g. it is lazy, and I believe more efficient in its memory and CPU usage than your implementation above.

iagwanderson20:01:38

something like that maybe:

(defn irange-2 [start end]
  (let [direction-positive? (< start end)]
    (if direction-positive?
      (range start (+ end 1))
      (range start (- end 1) -1))))

lennart.buit20:01:20

(nitpicking: (inc end) and (dec end) read more natural to me)

lennart.buit20:01:37

Iโ€™d also point out that you can call range with 0-3 arguments, maybe you want to also implement the 3 argument version (`start` end step )

roguas20:01:54

@ I was just tailoring it for one of the advent of code excersises, so I consider this "one-off". Plus step seems unintuitive considering negative numbers. I would much rather change the implementation to accepting a partially applied function as step - seems more natural to me, but I am noob so :)

lennart.buit20:01:42

Right, if its a one off then sure ^^

alexmiller20:01:35

I spent a couple months implementing the current version of range in core. I felt somewhat insane after a while just doing nothing but counting numbers more efficiently but there are a surprising number of weird edges and perf optimizations

lennart.buit20:01:34

now I am intrigued, the implementation is in the runtime?

alexmiller21:01:51

Well thatโ€™s all the most optimized one for all longs

alexmiller21:01:23

You can use range with any kind of number - rationals, doubles, bigintegers, and even mix them

alexmiller21:01:22

Consider long steps that are a high percentage of the long range etc - lots of overflow cases

alexmiller21:01:19

The long range has to support 3 different iteration styles - self reduce (highly optimized), chunked seqs, and unchunked seqs

alexmiller21:01:38

Balancing all that was a challenge

alexmiller21:01:50

And we were really trying to improve the existing common case, and add the optimized reduce behavior, and not break anything that worked in the much simpler prior impl

lennart.buit21:01:18

what made you tackle performance of range? Youโ€™d say that is not the most โ€˜hotโ€™ code to be found

alexmiller21:01:42

And it has to be thread safe (which I broke in one very subtle case and had to fix)

alexmiller21:01:19

range is used in lots of places for very small ranges, and people use for a huge set of examples

alexmiller21:01:03

But really it was part of a broader effort when we introduced transducers to make some of the key seq generator functions self reducible (range, iterate, cycle)

lennart.buit21:01:14

a class of problems I hope to be dealing with some day

didibus21:01:40

Wait, re-implementing range was better than doing (range 2 -3 -1) ?

andy.fingerhut21:01:45

I think it is more accurate to say "they preferred a different API that matched the way they wanted to pass parameters, versus what range provides"

roguas21:01:31

as Andy said, (range 2 -3 -1) returns (2 1 0 -1 -2), so now i need to append one more value by extending the range, again I have to do yet another if for which parameter i have to dec/inc and so and so

didibus21:01:50

Oh, okay, but still, why re-implement range and not just wrap it behind the interface you want say:

(defn rng
  [s e]
  (if (<= s e)
    (range s (inc e))
    (range s (dec e) -1)))

andy.fingerhut21:01:15

Which is one of my earlier suggestions in this thread

andy.fingerhut21:01:29

not the code, but the suggestion of calling range

didibus21:01:48

Oh okay, hadn't seen that

didibus21:01:15

I did always think it would be cool if range took an option to specify the bounds

didibus21:01:55

But then again, its so easy to just modify to wtv bound you most care about

didibus21:01:26

I think the default bounds are designed for zero based index iteration over collections.

roguas21:01:47

yes, i noticed range is mostly for iterating zero-index things, as for your solution it seems fine and better ๐Ÿ™‚ #beginners but you have more conditions, you will be (dec end) or (inc end) based on wether its negative or positive, so it still different code I think.

didibus21:01:09

Haha, ya sorry. I sounded a bit surprised, but it was mostly that I thought no one else had suggested to just leverage the existing range instead of re-implementing it, which I was wrong since others had I just overlooked it.

didibus21:01:57

What do you mean?

didibus21:01:31

(rng 5 1)
;; => (5 4 3 2 1)
(rng 2 -3)
;; => (2 1 0 -1 -2 -3)
(rng 1 4)
;; => (1 2 3 4)
(rng 0 -3)
;; => (0 -1 -2 -3)
(rng 2 2);; => (2)

roguas21:01:30

yep, I was doing(repling) the same, yep works as intended great, thanks

glfinn8320:01:26

Looking for a Cron library. I found Quartzite, but doesn't seem to be maintained anymore. Anybody here use it?

michael.e.loughlin21:01:27

is this idiomatic for java IO interop?

(let [raf (RandomAccessFile. "/tmp/longfile" "r")
      _ (.seek raf (* 1000 1000 1000))
      result (.readInt raf)]
  (.close raf)
  result)

michael.e.loughlin21:01:36

(example from Clojure Cookbook)

dpsutton21:01:26

Often youโ€™ll see โ€œdotoโ€ used for this use case

michael.e.loughlin21:01:29

I initially tried doto but that returns the final form - how do you return result from the above code?

dpsutton21:01:37

just for the seek

michael.e.loughlin21:01:51

and with-open for the .close call

dpsutton21:01:57

i think something like this

(with-open [raf (doto (RandomAccessFile. "/tmp/longfile" "r")
                  (.seek (* 1000 1000 1000)))]
  (.readInt raf))

michael.e.loughlin21:01:14

thanks, that makes sense

didibus21:01:01

I remember a scheduling library that just took a sequence of times, but I can't find it anymore, anyone knows?

didibus21:01:23

Oh, it might have been chime, except it seems its gotten more features now.

iagwanderson22:01:42

I use chime exactly like that

ramon.rios22:01:57

Guys i'm studying with a excellent book called "clojure for the brave and true"

ludvikgalois07:01:14

have you looked at #braveandtrue ?

ramon.rios22:01:45

These guys made me understand and really get excited about clojure

nate_clojurians23:01:57

that's a very good book

nate_clojurians23:01:19

a book I really liked was getting clojure