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

hindol03: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

@U883WCP5Z 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

bartuka00: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

bartuka00: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

bartuka00:01:11

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

bartuka00:01:28

and I've been doing this

bartuka00: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

bartuka00:01:34

that's fine

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.

💯 4
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.

👍 4
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

bartuka00: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

bartuka00: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

Gulli11: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.

Vachi11:01:33

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

Gulli12:01:39

Yeah seems pretty close. Thanks for that!

bartuka11: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

Alper Cugun12: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.

Alper Cugun12:01:33

Renamed to #books-clojure-applied

Ewan Valentine18: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

bartuka18:01:23

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

Ewan Valentine18:01:26

Oh my god, that was absolutely correct!

bartuka18:01:29

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

Ewan Valentine18:01:39

Thank you so much, it never occurred to me!

Ewan Valentine18:01:45

Ahaa that's really useful

bartuka18:01:55

what are you using to make the API call?

bartuka18:01:08

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

Ewan Valentine18: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

bartuka18:01:04

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

bartuka18:01:22

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

Ewan Valentine18:01:50

:key-fn keyword did the trick 🙂

🚀 4
jsyrjala19:01:09

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

mxm19: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

mxm20: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.

bartuka20: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)

parrot 8
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 )

mxm20:01:54

@UDF11HLKC 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

✔️ 12
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"

mxm21: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.

mxm21: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)

👍 4
mxm21:01:30

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

Gulli20:01:26

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

mloughlin21: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)

mloughlin21:01:36

(example from Clojure Cookbook)

dpsutton21:01:26

Often you’ll see “doto” used for this use case

mloughlin21: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

mloughlin21: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))

mloughlin21:01:14

thanks, that makes sense

👍 4
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.

bartuka22: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"

bravetrue 20
Probie07:01:14

have you looked at #braveandtrue ?

Ramon Rios22:01:45

These guys made me understand and really get excited about clojure

clj 12
Cora (she/her)23:01:57

that's a very good book

Cora (she/her)23:01:19

a book I really liked was getting clojure