why is here s unknown
(defn reverse-string
[]
''
[s]
(let [[first & rest] (seq s)]
(conj (reverse-string rest) first)))
what is [] ''
supposed to be?
I try to say that if the string is empty . it must be returning a empty string
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))))
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.
Then I think I need a if then in the [s] part and deleted the [] part
yeah. thats right..
actual: java.lang.ClassCastException: class java.lang.String cannot be cast to class clojure.lang.IPersistentCollection
`
(defn reverse-string
[s]
(if (empty? s)
""
(let [[first & rest] (seq s)]
(conj (reverse-string rest) first))
))
😞
Yeah, conj
wont work here (`cons` does).
I mean youll get a nested list of lists, but the order will be correct
do I
(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
so I think back to the drawing board
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
oke, it compiles but does not reverse ir
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.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`)
(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)...
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?
(->>
[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?str is your friend 🙂
what do you want in the end? The number of pair-wise equal elements?
I would write
(->> (map (fn [a b] (if (= a b) 1 0)) strand1 strand2)
(reduce +))
if you really want the threading to start with the data:
(->> [strand1 strand2]
(apply map (fn [a b] (if (= a b) 1 0)))
(reduce +))
(sorry, the indentation is messed up)
@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))
;; => ([1 :a] [2 :b] [3 :c])
to combine two sequences into pairs:
(->> (map vector '(1 2 0 7 4) '(:a :b 0 7))
(map (fn [[x y]] (if (= x y) 0 1)))
(reduce +))
or
(->> [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.@mathias_dw if I'm reading OP correctly, it's the number of pairwise differing elements 🙂
Ah yes :)
Thank you for all the replies. I think I get why I failed. The remove is interesting, I should remember it.
Thanks, did not think about that one
but replacing cons with str does do the job
Copying from Cursive channel: https://clojurians.slack.com/archives/C0744GXCJ/p1609605441000500
Is cursive "better" then vs code with calva for learning clojure and later maybe use a web framework ?
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.
oke, then I think I stay with vs code and calva
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!
@seancorfield there you are completety right
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?
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.
Is it possible to see which values have been run by a memoized functions to avoid collisions?
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
does it answer your question?
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
as long as the map key doesn't have any mutable data, that seems idiomatic to me
Uncloaked (;p
and efficient? I'll have a certain volumetry to adress
hash maps are immutable and their hash value is cached
Is there a way to call a function without arguments that gives a new value everytime? like 1,2,3,4,5
"replace the map with some hash mechanisms" is just duplicating the work clojure.core already does
ah ok
(fn [] (rand-int 6))
it is clearly answering my querstion, @noisesmith
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
yes, but it would be possible to get 1,3,1,4,5,1
I was thinking about increment everytime I call it. To give something a unique ID
@christiaaan You can use let
-over-`defn`:
(let [value (atom 0)]
(defn values [] (swap! value inc)))
Then each call returns a new incremented value.yes, clear
but I need some stress tests before arriving to that point
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))))
That sounds really good. I have no idea what happens there, though
What does atom do?
atom
is for managed mutability in Clojure.
okay, I should read more about this
swap!
applies the function to the current value "atomically"
and importantly in this case, it returns the new value
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.
With the code form @noisesmith I have to ((value-generator)) and it returns 1, if I call it again it's still 1.
or do I call it in the wrong way?
it is because value-generator is a builder
you need to call it once, the function which is given has the expected behavior
@christiaaan eg. (def values (value-generator))
- then you have something you can call to get numbers
but the point is its meant to be more flexible, so you don't need to bind it in a def
but if you just need a def @seancorfield’s original version is better
(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))
;; => 1
((test))
;; => 1
e.g.,
(defn -main [& args]
(let [a-values (value-generator)
b-values (value-generator)]
... (a-values) ...
... (b-values) ...
...))
so now you can have two streams of values.@christiaaan you're creating a new object and calling it once and throwing it away each time
you need to hold onto the object in order to call it again
(ins)user=> (def values (value-generator))
#'user/values
(ins)user=> (values)
1
(cmd)user=> (values)
2
So I need a global variable that I call again and again?
no, see @seancorfield’s example
@christiaaan See my -main
example.
you need to hold onto it in some context and reuse it, that's all
You can pass the value streams into functions you call from -main
Maybe I think to object oriented about that 😕
if you think objected oriented about it it makes perfect sense
it's a factory returning a function with a memory
you need a handle to the function returned in order to use that memory
So Sean's example is wrapped in the main function and has a permanent handle on the builder
not permanent, but scoped to the body of the let inside -main, but yes
it's not the builder he holds on to either - the builder is global
I don't have that in my exercise, that's what is causing me problems
he has a handle to the thing built
(unlike what you did, building a new one with a fresh memory every time)
I think I have an Idea, will come back to you. Thank you very much for the patient explanation
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
@christiaaan value generator can also be used like this (repeatedly captures the function and uses it... repeatedly to generate a sequence)
user=> (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)
@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)
yeah, for higher order functions that's often the right thing
Would range remember the last value it gave me?
can you post the exercise you are working on?
no, but inside a higher order function you often don't need to use state like that
it's a common fp technique to replace hidden state / generators with iterative consumption of a sequence in a HoF
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."
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] ;; <- arglist goes here
;; your code goes here
)
(def firstbot (robot))
(firstbot)
;; => 1
(def secondbot (robot))
(secondbot)
;; => 2
I find it extremely strange that robot returns bot-serial-number instead of calling it
It's just the start. In my first draft I took a random value and made the name-string from it.
like, if you called secondbot again, it would return 3
(so would firstbot, if you called that first)
My idea was, that i can get a new Id this way, when I have to rename it
(= firstbot secondbot) should return true
you haven't assigned anything
you've shared copies of a generator
hm, good argument
by "should" above I mean, that's how I read your code, I don't actually think that's a good idea
Me neither, now that you mentioned it
(defn robot []
(bot-serial-number))
(def firstbot (robot))
firstbot
;; => 1
(def secondbot (robot))
secondbot
;; => 2
(= firstbot secondbot)
;; => false
Now they have unique IDs.
also, "they should not follow a predictable sequence" seems to me to disallow this autoincrementing approach you're attempting here
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
i don't think that will necessarily prevent collisions though. but my knowledge of random number generators and seeds is pretty superficial
I could just generate a list, randomize it and pick the nth element via id...
a lazy permutation of [A-Z]{2}\d{3} could do the trick
i doubt that the integer sequences generated by different seeds are distinct like that
(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}
here using seeds 23 and 24 there's a common element to the first 50,000 elements
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.
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.
This is predictable. So I use a permutation of that list.
To keep memory in check, best case would be to make that list a lazy seq
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
you would have to know the permutation of that list to predict the next name
as the id is unique and every increasing, I could not pick a name twice, no need to remember.
"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.
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.