Hello everybody, Clojure noob here. I have been kicked off 4Clojure Android app and can't login to my user anymore, however credentials are correct and he browser access goes smooth as usual. Has anyone experienced the same behaviour? Thanks sincerely in advance!
oke, and another problem
(defn isPrime? [n known]
(loop [cnt (dec (count known)) acc []]
(if (< cnt 0) (not (any? acc))
(recur (dec cnt) (concat acc [(zero? (mod n (nth known cnt)))])))))
(defn my_primes
"my version to print out primes to a certain number"
[limit]
(map #(isPrime? % []) (range 2 .. limit ))
)
error :
; Syntax error compiling at (chapter4.clj:32:26).
; Can't take value of a macro: #'clojure.core/..
You don't need the ..
in your range function, and in fact ..
means something else in Clojure
Just do (range 2 limit)
thanks
and there is another error. I was hoping on seeing all the prime-numbers but I see now a collection of false 😢
you want filter
not map
I would say test your isPrime?
function first and make sure it returns true
when you give it a prime number and false
(or nil
) when you give it a non-prime number.
I tried that one also but then I see a empty collection. So I assume my isPrime is wrong.
yep, that one is not good. It gives false on (isPrime 3 [])
so back to the drawing board how I can test if a number is a prime
what do you think of my solutions . I did only study the 4 first chapters of the Clojure from the ground up site
https://github.com/RoelofWobben/Learning_in_public/blob/main/ground_up/src/ground_up/chapter4.clj
The code looks good overall. A couple style points: you are using partial
correctly, but there was a discussion recently about using (partial foo)
versus using #(foo %)
and a lot of folks favor using #(foo %)
in most cases because it’s more versatile and also slightly more efficient. Also it’s idiomatic to use shorter names like coll
and pred
instead of spelling out collection
and predicate
(though I favor using longer, more explicit names personally, except for really common terms like coll
and pred
).
oke
so If I understand you well I could change this (partial divisible? n)
to #(divisible? n)
?
You need to add the %
at the end
but yeah, that’s what I was referring to
oke, so #(divisible? n %)
?
Right
Oh, and it’s also conventional to use dashes in names instead of underscore.
so count-occurences
instead of count_occurences
, and my-filter
and my-primes
Clojure folks are too lazy to hold down the shift key 😄
thanks, changed also
I know there are also languages who use countOccurences
. I do not like that
but clojure folks love parentheses 😛
🙂
It’s actually the same number of parens in a lot of cases, it’s just that the first paren comes to the left of the function name instead of to the right.
Just like HTML does with <
and >
.
now take a break and tomorrow macros
seems to be a difficult subject
im following the plan which clojurefarm has made
I hope i once learn as much clojure I can make a website with it and solved the difficult challenges of Advent of Code or exercism
Macros can be really hard to wrap your head around, but honestly I've been using Clojure for years and years, and I've written maybe 3 macros total (not counting "let's write a macro!" exercises)
oke
I have read that you almost never write a macro
but there are three challenges at the end that you challenge to write a macro I think
Using the threading macros, find how many numbers from 0 to 9999 are palindromes: identical when written forwards and backwards. 121 is a palindrome, as is 7447 and 5, but not 12 or 953.
Write a macro id which takes a function and a list of args: (id f a b c), and returns an expression which calls that function with the given args: (f a b c).
Write a macro log which uses a var, logging-enabled, to determine whether or not to print an expression to the console at compile time. If logging-enabled is false, (log :hi) should macroexpand to nil. If logging-enabled is true, (log :hi) should macroexpand to (prn :hi). Why would you want to do this check during compilation, instead of when running the program? What might you lose?
Yeah most of the time a good function is all you ever need. Plus you can't pass a macro around like you can a function.
Two of those require you to write your own macro; the first one just wants you to use some of the built-in macros.
Just remember: macros write code, they don't do anything to your runtime data.
A macro is a tiny program that writes bits of other programs.
we see tomorrow. I hope I can ask for help here if needed or for a review. I hope I will not ask for help too much but the error messages are for me not always clear and as I said im totally new in Clojure so I think I need a lot of help to learn it wel
oke
Yeah, the error messages are a sore spot but they're getting better, and after a while you kind of get to recognize patterns of "Oh, I got this kind of error message, I should check, that"
but some of them stump even the best of us.
so this one makes the function (f a b c) ??
Yeah, that's a good use for a macro.
"Build Your Own defn
" 🙂
sorry I mean this one :
Write a macro id which takes a function and a list of args: (id f a b c), and returns an expression which calls that function with the given args: (f a b c).
it schould look like this It hink
(defmacro id [fun a b c]
(fun a b c)
You’ll need to add some macro-specific syntax to that so that it doesn’t just execute (fun a b c)
when you call it, but that’s actually a good start: you’ve written out what you want the end result to be when your macro executes, and now you just have to convert it into the body of an actual macro.
hmm, the only thing I can find it to change it into this :
(defmacro id [fun a b c]
(fun a b c)
``
You’re short a )
at the end, but once you add that, try running in your REPL with (macroexpand '(id foo a b c))
Then try (macroexpand '(id + 1 2 3))
on both I see this in repl
clj::chapter4=>
(chapter4/fun chapter4/a chapter4/b chapter4/c)
no idea if this is allright
For the second one you should see (+ 1 2 3)
, (possibly with the clojure namespace attached to the +
symbol). So there’s something not quite right with your macro.
I think so
you need unquotes
~a ~b ~c
you are currently putting a literal symbol a b c in the expanded code
then I think I also need to unquote the fun
yeah sorry, that too
(defmacro id [fun a b c]
`(fun ~a ~b ~c))
gives now with :
(macroexpand '(id + 1 2 3))
clj::chapter4=>
(chapter4/fun 1 2 3)
You’re getting closer
yep, unquote everything does the job
(defmacro id [fun a b c]
`(~fun ~a ~b ~c))
is the solution
I learn here more then on the clojurefarm channel 🙂
If you want a challenge, you could try to modify your id
macro so that it takes zero or more arguments.
or not to use syntax-quote
A macro without a syntax quote?? 😱
syntax quote is never needed
Heh, I can see how to do that but still 😱
with variable amount of arguments
(defmacro id [fun & args]
`(~fun ~args))
Close, you need a slightly different unquote for the args
bit
chips , I see
clj::chapter4=>
(+ (1 2 3))
Do you have a reference/tutorial for defmacro
? It should show you the list of unquote symbols.
I onu have this page : https://aphyr.com/posts/305-clojure-from-the-ground-up-macros
I tried rhis :
(defmacro id [fun & args]
`(~fun ~& args))
but that one gives a syntax error
have you tried ~@args
?
the @
essentially is apply
for macros
yep
also a syntax error :
(defmacro id [fun & args]
`(~fun ~&args))
That’s the “unquote-splice” symbol, btw, and notice it’s @
not &
a macro is literally just a function that takes code (as a data structure) and returns replacement code (as a data structure)
yes, this does it :
(defmacro id [fun & args]
`(~fun ~@args))
without the unquote I do not have a idea how to make that work as a beginner
so you just want a function that does: (id + 1 2 3) -> (+ 1 2 3)
yep, that is I the challenge as I understand it
args is a seq (1 2 3) so you're almost there. how do you prepend something to a list?
conj
there you go (or cons)
(defmacro id [fun & args]
(conj args fun))
that simple. I was overthinking it
or
(defmacro id [fun & args]
(cons fun args)) ;; order is different
people often think that macros are this special thing in clojure that needs syntax-quote and vice versa, but macros are literally just functions, and syntax-quote is literally just a templating syntax for data
Time to make a lot of notes and take a break. Tomorrow further on this for me unknown territory
you can find examples of both styles in clojure core
look at (source when)
or (source and)
for a couple good examples to study
hmm, in your examples source is unknown when I do it in repl @alexmiller
a good exercise is also to forget about defmacro
and just implement normal functions with defn
, and just invoke the function with quoted arguments manually (e.g. (foo 'bar '1 '[1 2 3])
instead of (foo bar 1 [1 2 3])
)
then to turn foo
into a macro you just need to remove the quotes from the invocation and replace defn
with defmacro
you might need to (use 'clojure.repl)
if you're not in user namespace
source
is in clojure.repl
thanks
This help from this channel gives me hope. I maybe can learn clojure
Time for a break
but im impressed by the speed
maybe somewhere in 2021 I will be able to try to solve AOC challenges on my own
but till I be there I have a lot to learn
you're almost done
What do you think of the brave book
:thumbsup:
it's all just more functions
almost done. I m now busy for 2 days.
Most people who have used the Brave & True book have been pleased with it
there’s also a #braveandtrue channel here specifically for that book/website
Can image I know now enough to do exercism or AOC puzzles
Keep https://clojure.org/api/cheatsheet open
and I have to learn a framework if I want to make websites and learn how to store something in a database
I cannot image that I now almost done , sorry
It took me a few months to get really comfortable with Clojure, but that was back before Slack and also I was new to the whole “functional programming” thing.
I tried to start using Clojure when it was really new, but the lack of leiningen, etc., at the time made it too painful to be productive, and gave it up for a couple of years. Then came back to it when the tooling was much improved.
Yeah, leiningen was a big help at the time.
im comimng from ruby and haskell so the functional part is not new to me
I have to get used to the parentheses and the polish notation as they call it
I had been using Ocaml quite a bit before Clojure, so same here regarding the functional aspect.
I think in the future I will have the most problems when I want to use something as reframe. Never wrote javascript or react or something before
but we see when we get there
I love re-frame, but yeah react does have a few quirks, and also cljs isn’t 100% identical to clj, so there’s bits that may bite the unwary. I’m working full time doing clj+cljs+re-frame tho, so there’s more than plenty enough to get a decent web app up and running.
im thinking if and when I got to that point to make a website which uses the Rijksmuseaum api . where someone sees a page and in the same time the images are downloaded and they replace the placeholders
Hmm, I’ve done that in straight JS back in the day, never tried to do in cljs :thinking_face:
replace placeholder images with hi-res versions that is
is there then a big difference in cjs and reframe. The way the clojurefarm is teaching us clojure is using reframe
Re-frame is a library that uses CLJS. It’s not part of Clojure or CLJS, it’s just popular.
What is clojurefarm?
Basically, React exists in the world of JS, and someone wrote a CLJS library named “reagent” to make it easier to use React in CLJS, and then someone else wrote a library named “re-frame” on top of reagent.
@claudius.nicolae see this page : https://github.com/athensresearch/ClojureFam
oke, I see when I get there
first this "book", then the brave book and then 4 clojure and then I think things like reframe i have to learn
and im not in a hurry because this is a hobby of mine and not for a job
no idea if there are clojure jobs in the Netherlands
maybe I come tomorrow or later back to see how to solve this one :
Advanced) Using the rationalize function, write a macro exact which rewrites any use of +, -, *, or / to force the use of ratios instead of floating-point numbers. (* 2452.45 100) returns 245244.99999999997, but (exact (* 2452.45 100)) should return 245245N
Greetings! Can't find an elegant way to do a partial flatten, by which I mean a function that accepts any nesting of sequences and returns a sequence of the innermost sequences ; e.g. (partial-flatten [[[1 2]] [[[3 4]]]]) => ([1 2] [3 4]
.
Could a kind puzzle-inclined person help me out?
Don't
This is exactly why using flatten of any stripe is a bad idea
you could do it with either tree-seq (save only colls that don't contain colls) or postwalk (concat any colls that only contain colls)
Care to elaborate?
because you always end up with "I want to flatten this, but...."
but I only want to flatten n levels, I only want to flatten when the lists don't contain x, etc
Are there here people who are doing the AOC challenges ?
in general flatten get used when you write a program of the form 1. do some stuff that builds up some nested list 2. flatten things out
but while step #1 is a generally steeped in context, by the time you get to step #2 the context has been thrown away, so determining how and where to flatten has to be done all over again
@roelof See the #adventofcode channel in this Slack.
while if you just applied concat here and there while doing #1, you had the context right there to easily make the decisions
Fair point. In this instance, the nesting comes from using (for) in a (loop ...) which ends up nesting the results arbitrarily - or rather, in an input dependent, yet useless way.
It would be indeed better not to use flatten-but-x to solve this. OTOH, using for [ ... :let ... :when ... ]
is super terse and expressive.
for doesn't arbitrarily nest things
you are recursively calling a function which uses for
for nests things once. the recursion over it can be n-deep with varying ns
right
it's a for [ ... ] recur
the context here is you know when you recur, so unwrap it there
I tried to do that, but probably got it wrong. I'll do more attempts rn.
for example, if you have (for [i (f x)] i)
and (f x)
returns a lists of lists then do (for [i (f x) ii i] ii)
it's more like (defn f [a prefix] (comment omitted termination code)) (for [n (magic a)] (f (drop n a) (conj prefix (first a))))). (magic) returns a small number.
right, so shift the recursive call up into the for
(defn f [...] ... (for [... i (f ...)] i))
ha.
that will concat one level of lists for every recursive call, so in your base case you may need to add a second layer of lists if your lists is meant to be a list of lists
the recursive call chain ends up being exactly the context information you would need to recover in your "flatten, but..."
that worked.
it better, I am a big fan of recursive fors like that
I followed your instructions and that made it work. I don't understand how or why it works tho :thinking-face:
for is a combination of a lot of difference sequence functions, include map and mapcat
(for [i x] (f i))
=> (map f x)
so far so good.
(for [i x ii (f i)] (g ii))
=> (map g (mapcat f x))
(for [x xs y ys] [x y]) => ([x0 y0] [x0 y1] .. [x0 yN] [x1 y0] .. [xN yN])
- cartesian product I believe
(for [x xs y ys] [x y])
=> (mapcat (fn [x] (map (fn [y] [x y]) ys)) xs)
oooh.
[ processing intensifies ... ]
any for that doesn't use any of the keyword stuff (:when, :while, etc) can be transformed into an expression of nested mapcats around an inner map, you can do the keyword stuff to you just have to sprinkle in some calls to filter and take-while
(and of course (map f x)
<=> (mapcat (comp list f) x)
)
eloquently put.
this belongs to clojuredocs!
(macroexpand '(for [x xs y ys] [x y]))
is significantly harder to grok thank your explanations 🙂
Thank you very much for your time and eloquent explanation
is there a easy way to make this work :
(ns chapter5)
(defn schedule
"Using the control flow constructs we've learned, write a schedule
function which, given an hour of the day, returns what you'll be
doing at that time. (schedule 18), for me, returns :dinner."
[my-time]
(cond
(<= "0:00" my-time "07.00") :sleeping
(== "07:00" my-time) :breakfast
(<= "07:01" my-time "12:00") :work
(= "12:00" my-time) :lunch
(<= "12:01" my-time "18:00") :more-work
(<= "18:01" my-time "19:00") :dinner
(<= "19:01" my-time "23:59") :watching-tv
))
is there a way I can convert the given time and the comparisaion to a time object
Right now I only have studied the first 5 chapters of the "Clojure from the ground up " tutorial
I find java time tricky to work with, I think for this particular exercise I’d convert the numeric input to a string
@roelof take a look at the compare
function. For just this limited set of string comparisons, it should be adequate.
isn't compare what <= is using already?
never mind, it only works on numbers
Hmm, have to think how compare could help me
@manutter51 the trick to make it all string could help
you can do two comparisons per case, or if you feel clever do a binary search
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/LocalTime.html
?? I want to check if the given time is between two other times
right, and using compare twice can tell you if it is
this is so far the best date-time api i've ever worked with. so if you want to do it right (like how you would do it in a real-world project), i would suggest taking a look at this api.
if they are strings
oke
Why not just represent time of day in minutes as a number? No need to introduce string comparisons here
(defn time-as-minutes [hour minute] (+ minute (* 60 hour))
(defn schedule [my-time-in-minutes]
(cond
(<= (time-as-minutes 0 0) my-time-in-minutes (time-as-minutes 7 0)) :sleeping
;; add the rest of the cases
))
for a given pair of times, you'd want the first compare to return positive or zero, and the second compare to return negative or zero
so I can do (localtime my_time)
to convert the given time to a time object
so I have to that for all the times
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/LocalTime.html#parse(java.lang.CharSequence) would be right method to call.
@trailcapital that is also a good idea
(java.time.LocalTime/parse "07:00")
#object[java.time.LocalTime 0x2b87581 "07:00"]
(str *1)
"07:00"
oke, and then I can use isbefore
and isafter
to check if its in the interval
Time to play with this idea
yup
just as an example, for the first clause in your cond
:
(def midnight (java.time.LocalTime/MIDNIGHT))
#'user/midnight
(def breakfast-time (java.time.LocalTime/parse "07:00"))
#'user/morning
(def my-time (java.time.LocalTime/parse "06:30"))
#'user/my-time
(and (.isAfter my-time midnight)
(.isBefore my-time breakfast-time))
true
oke I was trying another idea but get errors
(ns chapter5)
(defn parse-time
"Parse a string to a time object"
[time-string]
(java.time.LocalTime/parse time-string))
(defn schedule
"Using the control flow constructs we've learned, write a schedule
function which, given an hour of the day, returns what you'll be
doing at that time. (schedule 18), for me, returns :dinner."
[my-time]
(cond
(<= (parse-time "00:00") (parse-time(str my-time)) (parse-time "07:00") ) :sleeping
))
(schedule "03:00")
error :
; Execution error (ClassCastException) at chapter5/schedule (form-init11789170975546566471.clj:9).
; class java.lang.String cannot be cast to class java.lang.Number (java.lang.String and java.lang.Number are in module java.base of loader 'bootstrap')
If you wanted to use the built-in (jvm) time library, perhaps this might be suitable...
(ns foo
(:import [java.time LocalTime]))
(defn parse
[my-time]
(LocalTime/parse my-time))
(def breakfast (parse "07:00"))
(def midday (parse "12:00"))
(def nearly-dinner (parse "18:00"))
(def dinner (parse "19:00"))
(def late-night (parse "23:59"))
(defn schedule
"Using the control flow constructs we've learned, write a schedule
function which, given an hour of the day, returns what you'll be
doing at that time. (schedule 18), for me, returns :dinner."
[my-time]
(let [my-time' (parse my-time)]
(cond
(.isBefore my-time' breakfast) :sleeping
(= my-time' breakfast) :breakfast
(.isBefore my-time' midday) :work
(= my-time' midday) :lunch
(.isBefore my-time' nearly-dinner) :more-work
(.isBefore my-time' dinner) :dinner
(.isBefore my-time' late-night) :watching-tv)))
(schedule "06:00")
(schedule "07:00")
(schedule "11:00")
(schedule "12:00")
(schedule "15:00")
(schedule "18:00")
(schedule "19:30")
(schedule "00:00")
oh, you cannot use <=
with times...
it's simply not overridden for those java time types...
take a look at the example for how to do date-time comparison that i posted above....
Could also be working
I will play tomorrow with both ideas
oke, thanks , I will play with it tomorrow. its too late
rule of thumb for when dealing with time: don't roll your own solutions because time is hard. 😉
why do I get this error:
(ns chapter5
(:import [java.time LocalTime]))
(defn parse
[my-time]
(LocalTime/parse my-time))
(def breakfast (parse "07:00"))
(def midday (parse "12:00"))
(def nearly-dinner (parse "18:00"))
(def dinner (parse "19:00"))
(def late-night (parse "23:59"))
(defn schedule
"Using the control flow constructs we've learned, write a schedule
function which, given an hour of the day, returns what you'll be
doing at that time. (schedule 18), for me, returns :dinner."
[my-time]
let [my-time' (parse my-time)]
(cond
(.isBefore my-time' breakfast) :sleeping ))
(schedule "03:00")
error :
unresolved symbol my-time'
I never try to roll my own solution when there is something for it
Need parens for the let?
one exception is when your objective is learning how to do it. 🙂
yes, you're missing a parens just before the let
, i.e., (let...
chips
now another one :
Could not locate chapter4__init.class, chapter4.clj or chapter4.cljc on classpath.
which do not make sense because chapter4.cjs in another file in the same directory
cjs
?
sorry clj
how did you trigger this error?
re-opened vs code and then do crtl-alt-c crtl-alt-j
to start up a calva repl
I feel like you are not the first person here with this same error, wonder if maybe whatever repo you're using is not set up well
what command does ctrl-alt-c ctrl-alt-j
run?
could be. I can push all my code to github so you can look ?
but it's saying exactly the problem - you're trying to load chapter4 ns but it's not on your classpath. presumably you have the file so it's something with the config of the classpath
this is what the maintainer and creator of calva mentioned yesterday to you
@roelof I’d be happy to try your project out from the Calva perspective of things.
As @alexmiller mentions, this problem shows up now and then. Might be a problematic project template, but also might be a Ux issue with Calva.
I know but calva did not even start up
this is my repo so far : https://github.com/RoelofWobben/Learning_in_public
yep, as one of the challenge was to make my own filter method
your classpath is by default "src" so requiring chapter4
will look for src/chapter4.clj
but that file is located at src/ground_up/chapter4.clj
oke, I can do that
the namespace needs to be ground-up.chapter4
or the file can be moved out of the ground_up directory to just src
or you can add src/ground_up
onto your classpath and leave the namespaces alone
I did this because the next step in the course is the brave book and I do not want two chapter 3 in the repo
thanks, everything is working fine now
Gn , really time to sleep