beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
roelof 2021-01-02T10:18:27.291300Z

why is here s unknown

roelof 2021-01-02T10:18:33.291600Z

(defn reverse-string  
  []
  ''
  [s]
  (let [[first & rest] (seq s)]
    (conj (reverse-string rest) first)))

bronsa 2021-01-02T10:21:35.292Z

what is [] '' supposed to be?

roelof 2021-01-02T10:24:22.292700Z

I try to say that if the string is empty . it must be returning a empty string

Kannan Ramamoorthy 2021-01-02T11:10:57.294700Z

I assume you are trying to create a mult-arity function one for no-arg and another for normal string. Just to correct the syntax, your function would look like this.

(defn reverse-string  
  ([]
  "")
  ([s]
  (let [[first & rest] (seq s)]
    (conj (reverse-string rest) first))))

Kannan Ramamoorthy 2021-01-02T11:12:17.296300Z

The function contain logical error, the zero arity variant will not be called for empty string/seq rather when it is called with no argument. I leave it upto you to experiment.

roelof 2021-01-02T11:21:47.296800Z

Then I think I need a if then in the [s] part and deleted the [] part

Kannan Ramamoorthy 2021-01-02T11:24:13.297Z

yeah. thats right..

roelof 2021-01-02T11:35:02.297500Z

actual: java.lang.ClassCastException: class java.lang.String cannot be cast to class clojure.lang.IPersistentCollection

roelof 2021-01-02T11:35:15.297800Z

`

(defn reverse-string  
  [s]
  (if (empty? s)
    ""
    (let [[first & rest] (seq s)]
      (conj (reverse-string rest) first))
    ))

roelof 2021-01-02T11:35:17.298Z

😞

Jakub Zika 2021-01-02T11:48:03.302Z

Yeah, conj wont work here (`cons` does).

Mno 2021-01-02T11:50:01.302900Z

I mean youll get a nested list of lists, but the order will be correct

roelof 2021-01-02T11:50:12.303200Z

do I

roelof 2021-01-02T11:50:33.303700Z

(defn reverse-string  
  [s]
  (if (empty? s)
    ""
    (let [[first & rest] (seq s)]
      (cons (reverse-string rest) first))
    ))


(reverse-string "robot")
gives now
; Execution error (IllegalArgumentException) at reverse-string/reverse-string (form-init5861935267258777464.clj:8).
; Don't know how to create ISeq from: java.lang.Character

roelof 2021-01-02T11:50:46.304100Z

so I think back to the drawing board

Joe 2021-01-02T12:30:27.307400Z

Check out the argument order forhttps://clojuredocs.org/clojure.core/cons - it's the other way around from https://clojuredocs.org/clojure.core/conj: collection last instead of first

roelof 2021-01-02T12:47:23.307600Z

oke, it compiles but does not reverse ir

Joe 2021-01-02T12:58:06.309800Z

No, because (*cons* \r '(\t \o \b \o)) will put r at the start 🙂 You could check out the different between the outputs of these to see the difference between cons and conj on different types of collections:

(cons \r `(\t \o \b \o))
(cons \r `[\t \o \b \o])

(conj `(\t \o \b \o) \r)
(conj [\t \o \b \o] \r)
But I think what you want to get out of your recursive function is a string, correct? If so I would think it would be better to have the recursive part of your function use string concatenation rather than collection operators.

👍 2
pavlosmelissinos 2021-01-02T13:27:48.314900Z

I'm beginning to realize I don't understand time. 😄 Assuming {:deps {clojure.java-time/clojure.java-time {:mvn/version "0.3.2"}}} and (require '[java-time :as t]) : 1. What's the correct way to compare a t/local-date to a t/instant? t/before? and t/after? don't seem to work on those types. e.g. this doesn't work: (t/before? (t/local-date "<http://dd.MM|dd.MM>.yyyy" "31.12.2020") (t/instant)) => class java.time.Instant cannot be cast to class java.time.chrono.ChronoLocalDateTime 2. What's the correct way to convert a date string to a java-time instant referring to the beginning of that date (midnight)? e.g. as expected, this doesn't work either (t/instant "<http://dd.MM|dd.MM>.yyyy" "31.12.2020") because the time information is missing from the string representation (`Unsupported field: InstantSeconds`)

pavlosmelissinos 2021-01-02T13:30:05.315100Z

(t/truncate-to (t/local-date-time (t/local-date "<http://dd.MM|dd.MM>.yyyy" "31.12.2020")) :days) gives a local-date-time at the beginning of the day but I still haven't found a way to convert or compare that to an instant and the expression already seems way too verbose for what I'm trying to do (at least by clojure standards)...

Christian 2021-01-02T14:07:20.316700Z

I want to thread two list into a map function, but I cant figure out how. This is what I want to have in the end:

(reduce + (map #(if (= %1 %2) 0 1) strand1 strand2))
And this is what I tried. I was hoping for better readability. What do you think about that?
(-&gt;&gt;
   [strand1 strand2]
   (#(if (= %1 %2) 0 1))
   (map)
   (reduce +)
   )
As far as I understand all the work around threading is for better readability, isn't it?

2021-01-02T15:38:17.317600Z

str is your friend 🙂

2021-01-02T15:42:31.317800Z

what do you want in the end? The number of pair-wise equal elements?

2021-01-02T15:46:49.318800Z

I would write

(-&gt;&gt; (map (fn [a b] (if (= a b) 1 0)) strand1 strand2)
                            (reduce +))

2021-01-02T15:49:36.319Z

if you really want the threading to start with the data:

(-&gt;&gt; [strand1 strand2]
(apply map (fn [a b] (if (= a b) 1 0)))
                            (reduce +))

2021-01-02T15:49:48.319200Z

(sorry, the indentation is messed up)

2021-01-02T16:16:12.319400Z

@christiaaan First to note where your second snippet will fail to evaluate successfully: • the anonymous function will get a single argument [strand1 strand2] but expects two -- to fix that you'd need to apply it; • (map) after that will be evalutated as (map 0) or (map 1) -- since the function above returns a scalar • (reduce +) expects a collection as argument but will get a second function instead -- but at this point we already lost information we need to compute what the first snippet does. For the sake of exercise you can besides already suggested possibilities use e.g. (map vector s1 s2) :

(map vector '(1 2 3 4) '(:a :b :c))
;; =&gt; ([1 :a] [2 :b] [3 :c])
to combine two sequences into pairs:
(-&gt;&gt; (map vector '(1 2 0 7 4) '(:a :b 0 7))
     (map (fn [[x y]] (if (= x y) 0 1)))
     (reduce +))
or
(-&gt;&gt; [s1 s2]
     (apply map vector)
     (remove #(apply = %))
     count)
Can't say which is more readable, since it's somewhat subjective -- in this case I think I'd prefer the original expression. Ideally your environment should be able to help you evaluate and inspect intermediate values easily -- e.g. in the second snippet above, it's probably easier to proceed adding threading steps one by one, when you know that what you have so far works and can see what it produces.

2021-01-02T16:19:01.319700Z

@mathias_dw if I'm reading OP correctly, it's the number of pairwise differing elements 🙂

2021-01-02T16:19:41.319900Z

Ah yes :)

Christian 2021-01-02T16:30:39.320200Z

Thank you for all the replies. I think I get why I failed. The remove is interesting, I should remember it.

roelof 2021-01-02T16:40:37.320400Z

Thanks, did not think about that one

roelof 2021-01-02T16:41:05.320600Z

but replacing cons with str does do the job

noorvir 2021-01-02T17:01:59.321300Z

Copying from Cursive channel: https://clojurians.slack.com/archives/C0744GXCJ/p1609605441000500

roelof 2021-01-02T17:03:54.322400Z

Is cursive "better" then vs code with calva for learning clojure and later maybe use a web framework ?

Michael W 2021-01-02T17:15:07.324100Z

It's about the same, either one works great for learning, and they both work well in general. calva has a bit of an edge with documentation though.

roelof 2021-01-02T17:56:05.324700Z

oke, then I think I stay with vs code and calva

seancorfield 2021-01-02T17:57:48.326100Z

My general advice for beginners, as far as editors are concerned, is: use the editor you already know best if it has a Clojure integration (else use an editor that is at least similar to one you already know). It's helpful to not have to learn both Clojure and a completely new editor at the same time!

👍 12
roelof 2021-01-02T17:58:22.326500Z

@seancorfield there you are completety right

caumond 2021-01-02T20:36:42.328900Z

hi, I consider building a map with a complex key (a potentially complex value map). Will it be idiomatically efficient or need I to replace the map with some hash mechanisms?

caumond 2021-01-08T20:48:35.173600Z

Somehow I missed that messages. Thx @ben.sless , it s helpfull. @noisesmith up to now, I have no guarantee that the same value share the same object. Then re designed it and finally found a different approach to simplify my maps and more surely reuse the same value. That solution, not fully implemented yet, start to sound better!! Thx a lot.

Christian 2021-01-02T20:41:45.330Z

Is it possible to see which values have been run by a memoized functions to avoid collisions?

caumond 2021-01-02T20:44:07.331300Z

I'm not sure to catch your point, but I put the values in the map exactly to remember which one has already been executed and which has not

caumond 2021-01-02T20:44:29.331500Z

does it answer your question?

Christian 2021-01-02T20:48:55.333Z

I'd do so as well, but with this exercise I have no control about the arguments. I could define a mutable global map for that? Sounds very un-clojure

phronmophobic 2021-01-02T20:49:36.333200Z

as long as the map key doesn't have any mutable data, that seems idiomatic to me

caumond 2021-01-02T20:50:56.333500Z

Uncloaked (;p

caumond 2021-01-02T20:52:07.333600Z

and efficient? I'll have a certain volumetry to adress

2021-01-02T20:52:50.334300Z

hash maps are immutable and their hash value is cached

Christian 2021-01-02T20:52:56.334600Z

Is there a way to call a function without arguments that gives a new value everytime? like 1,2,3,4,5

2021-01-02T20:53:25.334700Z

"replace the map with some hash mechanisms" is just duplicating the work clojure.core already does

➕ 1
caumond 2021-01-02T20:53:53.335Z

ah ok

phronmophobic 2021-01-02T20:54:04.335300Z

(fn [] (rand-int 6))

caumond 2021-01-02T20:54:40.336100Z

it is clearly answering my querstion, @noisesmith

2021-01-02T20:54:44.336400Z

of course if the map hashing becomes a bottleneck you'll want a custom lookup (eg. indirection by id with an id generator?) but in that case the usage of the map itself will be an even bigger bottleneck in most cases

Christian 2021-01-02T20:55:06.337100Z

yes, but it would be possible to get 1,3,1,4,5,1

Christian 2021-01-02T20:55:34.337900Z

I was thinking about increment everytime I call it. To give something a unique ID

seancorfield 2021-01-02T20:55:45.338500Z

@christiaaan You can use let-over-`defn`:

(let [value (atom 0)]
  (defn values [] (swap! value inc)))
Then each call returns a new incremented value.

caumond 2021-01-02T20:56:43.339300Z

yes, clear

caumond 2021-01-02T20:57:06.339900Z

but I need some stress tests before arriving to that point

2021-01-02T20:57:23.340300Z

that also creates global top level singleton, so it often ends up evolving to one more level of nesting:

(defn value-generator []
  (let [value (atom 0)]
    (fn [] (swap! value inc))))

Christian 2021-01-02T20:58:09.340700Z

That sounds really good. I have no idea what happens there, though

Christian 2021-01-02T20:58:33.340900Z

What does atom do?

seancorfield 2021-01-02T20:58:58.341500Z

atom is for managed mutability in Clojure.

Christian 2021-01-02T20:59:16.342Z

okay, I should read more about this

seancorfield 2021-01-02T20:59:17.342100Z

swap! applies the function to the current value "atomically"

2021-01-02T21:00:01.342600Z

and importantly in this case, it returns the new value

seancorfield 2021-01-02T21:00:19.343Z

And you deref an atom to get the value out. swap! just happens to return the new value which is convenient for the case above.

Christian 2021-01-02T21:03:38.343600Z

With the code form @noisesmith I have to ((value-generator)) and it returns 1, if I call it again it's still 1.

Christian 2021-01-02T21:03:51.343900Z

or do I call it in the wrong way?

caumond 2021-01-02T21:05:40.344300Z

it is because value-generator is a builder

caumond 2021-01-02T21:05:56.345Z

you need to call it once, the function which is given has the expected behavior

2021-01-02T21:06:36.345700Z

@christiaaan eg. (def values (value-generator)) - then you have something you can call to get numbers

2021-01-02T21:06:54.346200Z

but the point is its meant to be more flexible, so you don't need to bind it in a def

2021-01-02T21:09:15.347700Z

but if you just need a def @seancorfield’s original version is better

Christian 2021-01-02T21:09:26.348100Z

(defn value-generator []
  ;; credit to seancorfield and noisesmith @ clojurians slack
  (let [value (atom 0)]
    (fn [] (swap! value inc))))

(defn test[]
  (let [id (value-generator)]
    id))

((test))
;; =&gt; 1

((test))
;; =&gt; 1

seancorfield 2021-01-02T21:09:33.348300Z

e.g.,

(defn -main [&amp; args]
  (let [a-values (value-generator)
        b-values (value-generator)]
    ... (a-values) ...
    ... (b-values) ...
    ...))
so now you can have two streams of values.

2021-01-02T21:10:08.348900Z

@christiaaan you're creating a new object and calling it once and throwing it away each time

2021-01-02T21:10:30.349500Z

you need to hold onto the object in order to call it again

2021-01-02T21:11:30.350200Z

(ins)user=&gt; (def values (value-generator))
#'user/values
(ins)user=&gt; (values)
1
(cmd)user=&gt; (values)
2

Christian 2021-01-02T21:12:24.350700Z

So I need a global variable that I call again and again?

2021-01-02T21:12:34.351100Z

no, see @seancorfield’s example

seancorfield 2021-01-02T21:12:36.351300Z

@christiaaan See my -main example.

2021-01-02T21:12:47.351800Z

you need to hold onto it in some context and reuse it, that's all

seancorfield 2021-01-02T21:12:52.352Z

You can pass the value streams into functions you call from -main

Christian 2021-01-02T21:13:18.352400Z

Maybe I think to object oriented about that 😕

2021-01-02T21:13:47.353200Z

if you think objected oriented about it it makes perfect sense

2021-01-02T21:13:54.353700Z

it's a factory returning a function with a memory

2021-01-02T21:14:13.354300Z

you need a handle to the function returned in order to use that memory

Christian 2021-01-02T21:14:32.354700Z

So Sean's example is wrapped in the main function and has a permanent handle on the builder

2021-01-02T21:14:54.355200Z

not permanent, but scoped to the body of the let inside -main, but yes

2021-01-02T21:15:31.356Z

it's not the builder he holds on to either - the builder is global

Christian 2021-01-02T21:15:31.356100Z

I don't have that in my exercise, that's what is causing me problems

2021-01-02T21:15:36.356300Z

he has a handle to the thing built

2021-01-02T21:16:00.356600Z

(unlike what you did, building a new one with a fresh memory every time)

Christian 2021-01-02T21:17:28.357500Z

I think I have an Idea, will come back to you. Thank you very much for the patient explanation

caumond 2021-01-02T21:19:13.359100Z

yes, the help given here is amazing. Thx all guys. I'm still not skilled enough to participate, but I'm on the way, (;p

2021-01-02T21:22:55.360400Z

@christiaaan value generator can also be used like this (repeatedly captures the function and uses it... repeatedly to generate a sequence)

user=&gt; (repeatedly 100 (value-generator))
(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100)

2021-01-02T21:25:34.362200Z

@christiaaan i was just going to say (before @noisesmith’s last post) that it sounds a bit like you should play with the lazy sequences in clojure a bit more. The previous answers are of course correct, but maybe what you're looking for is rather just something like (range)

2021-01-02T21:26:14.362800Z

yeah, for higher order functions that's often the right thing

Christian 2021-01-02T21:27:36.363200Z

Would range remember the last value it gave me?

dpsutton 2021-01-02T21:27:57.363800Z

can you post the exercise you are working on?

2021-01-02T21:28:06.364100Z

no, but inside a higher order function you often don't need to use state like that

2021-01-02T21:28:41.365500Z

it's a common fp technique to replace hidden state / generators with iterative consumption of a sequence in a HoF

Christian 2021-01-02T21:29:14.366200Z

I think I overthink it, but I want to make it work. https://exercism.io/my/solutions/17ad9da210054c20a08e31fd4987ea6a " Manage robot factory settings. When robots come off the factory floor, they have no name. The first time you boot them up, a random name is generated in the format of two uppercase letters followed by three digits, such as RX837 or BC811. Every once in a while we need to reset a robot to its factory settings, which means that their name gets wiped. The next time you ask, it will respond with a new random name. The names must be random: they should not follow a predictable sequence. Random names means a risk of collisions. Your solution must ensure that every existing robot has a unique name."

Christian 2021-01-02T21:30:16.366700Z

It works now, like I want it:

(ns robot-name)

(defn value-generator []
  ;; credit to seancorfield and noisesmith @ clojurians slack
  (let [value (atom 0)]
    (fn [] (swap! value inc))))

(def bot-serial-number (value-generator))

(defn robot []
  bot-serial-number)

(defn robot-name [robot] 
)

(defn reset-name [robot] ;; &lt;- arglist goes here
  ;; your code goes here
)

(def firstbot (robot))
(firstbot)
;; =&gt; 1
(def secondbot (robot))

(secondbot)
;; =&gt; 2

2021-01-02T21:31:31.367600Z

I find it extremely strange that robot returns bot-serial-number instead of calling it

Christian 2021-01-02T21:31:36.367700Z

It's just the start. In my first draft I took a random value and made the name-string from it.

2021-01-02T21:32:03.368300Z

like, if you called secondbot again, it would return 3

2021-01-02T21:32:24.368900Z

(so would firstbot, if you called that first)

Christian 2021-01-02T21:32:25.369Z

My idea was, that i can get a new Id this way, when I have to rename it

2021-01-02T21:32:54.369500Z

(= firstbot secondbot) should return true

2021-01-02T21:32:58.369800Z

you haven't assigned anything

2021-01-02T21:33:03.370Z

you've shared copies of a generator

Christian 2021-01-02T21:33:11.370300Z

hm, good argument

2021-01-02T21:33:42.370900Z

by "should" above I mean, that's how I read your code, I don't actually think that's a good idea

Christian 2021-01-02T21:34:01.371200Z

Me neither, now that you mentioned it

Christian 2021-01-02T21:35:13.372100Z

(defn robot []
  (bot-serial-number))


(def firstbot (robot))
firstbot
;; =&gt; 1

(def secondbot (robot))
secondbot
;; =&gt; 2

(= firstbot secondbot)
;; =&gt; false

Christian 2021-01-02T21:35:21.372500Z

Now they have unique IDs.

dpsutton 2021-01-02T21:35:22.372600Z

also, "they should not follow a predictable sequence" seems to me to disallow this autoincrementing approach you're attempting here

Christian 2021-01-02T21:36:13.373400Z

I wanted to use the id as a seed for some random number mumbo-jumbo. But I have not read that far into the rand-possibilites in clojure

dpsutton 2021-01-02T21:39:29.374Z

i don't think that will necessarily prevent collisions though. but my knowledge of random number generators and seeds is pretty superficial

Christian 2021-01-02T21:42:26.375700Z

I could just generate a list, randomize it and pick the nth element via id...

👍 1
Christian 2021-01-02T21:43:26.377400Z

a lazy permutation of [A-Z]{2}\d{3} could do the trick

dpsutton 2021-01-02T21:43:52.377800Z

i doubt that the integer sequences generated by different seeds are distinct like that

dpsutton 2021-01-02T21:45:30.378100Z

(let [r1 (java.util.Random. 23) r2 (java.util.Random. 24) f #(into #{} (take 50000 (iterator-seq (.iterator (.ints %)))))] (clojure.set/intersection (f r1) (f r2)))
#{1304654219}

dpsutton 2021-01-02T21:45:55.378500Z

here using seeds 23 and 24 there's a common element to the first 50,000 elements

dpsutton 2021-01-02T21:47:05.379300Z

and in fact its of course impossible to partition the longs into partitions like this as each long can't select a set of longs not included in another long's set due to the pigeonhole principle.

Christian 2021-01-02T21:48:45.380100Z

I was thinking: I make a list of every possible name from AA000 to ZZ999. Then pick the nth element, which is my bots id.

Christian 2021-01-02T21:49:06.380900Z

This is predictable. So I use a permutation of that list.

👍 1
Christian 2021-01-02T21:49:25.381600Z

To keep memory in check, best case would be to make that list a lazy seq

dpsutton 2021-01-02T21:49:39.381900Z

that sounds like exactly a "predictable sequence" the exercise is telling you to avoid. and you'd still need to remember which you have picked to avoid that choice. and if you're gonna keep a set of chosen bot names, there's no need for the original sequence

Christian 2021-01-02T21:50:15.382300Z

you would have to know the permutation of that list to predict the next name

Christian 2021-01-02T21:50:45.382800Z

as the id is unique and every increasing, I could not pick a name twice, no need to remember.

2021-01-02T21:51:45.384100Z

"predictable" is not a very precisely defined word, I don't think. What you can predict depends upon what you know about the code that is generating the sequence, really. If you know it is a simple pseudo-random number generator of several kinds, those are all deterministic, so if you know (or can infer from observing their outputs) the internal state, you can predict the rest of the sequence after that.

2021-01-02T21:53:10.385600Z

If you have some form of random number generator that you believe is difficult enough for others to predict, you can use it to generate random IDs, and simply keep trying if a generated one matches one generated earlier -- that works well as long as you don't use over about half of the possible values -- using too large a fraction of possible values will slow down that technique quite a bit.