This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-03-26
Channels
- # architecture (2)
- # beginners (310)
- # boot (34)
- # cider (50)
- # cljs-dev (82)
- # cljsrn (1)
- # clojure (125)
- # clojure-dusseldorf (1)
- # clojure-hamburg (1)
- # clojure-italy (47)
- # clojure-russia (21)
- # clojure-spec (38)
- # clojure-uk (36)
- # clojurescript (200)
- # community-development (21)
- # cursive (10)
- # datomic (15)
- # duct (58)
- # emacs (20)
- # fulcro (10)
- # funcool (1)
- # graphql (2)
- # hoplon (6)
- # jobs (1)
- # lumo (12)
- # mount (20)
- # off-topic (14)
- # om (5)
- # portkey (43)
- # protorepl (2)
- # re-frame (31)
- # reagent (36)
- # ring (17)
- # ring-swagger (6)
- # shadow-cljs (50)
- # spacemacs (9)
- # sql (5)
- # tools-deps (28)
- # uncomplicate (4)
- # unrepl (5)
- # vim (2)
- # yada (2)
@mfiano: there's a good lib for immutable randoms in clojure
@noisesmith I specifically needed a portably seedable PRNG with very particular number generation functions (so I wrote my own)
OK then
@dpsutton the specific use case of the linked library is to be able to concisely replicate generative test inputs that failed
That is the same stage should be generated from the same seed...that part needs to be deterministic because maps will be saved as the seed
Python and CL. I never used a Scheme before
Thank you. I've only been working on it for a couple days. I keep having to stop to read clojure docs being new as I am ๐
ok. reading it in common lisp, you mutate the stage instance. remember in clojure to just thread that stage map around. as an argument and then return it with either stuff added to it or not
Yeah, I get that. Infact...my ultimate goal is to store a list of all changes made to the stage so that I can play back the generation as an animation in Quil or something
Interesting, will do
@dpsutton You just confused me ha
I thought I was making a new function add-room-to-stage
that does that with a single room.
Oh was that a typo pluralization?
oh that was just pseudocode. i'm big on simple functions and descriptive names. for you it would be
(defn place-room
[stage room]
(update stage :rooms conj room))
place-room
is the function that iterates over the rooms cells and marks them as no longer being walls
As you can see by the nested doseq
oh. i saw that and saw it didn't do anything. the nested doseq doesn't return anything
Right, there's a TODO ๐
So with all these changes I don't get any output anymore
Stack trace of root exception is empty;
Thanks
with a null pointer excpetion
Seems to be a problem with that loop. commenting it out doesn't error
Thanks. One minute.
Ok I got it to compile but I don't know if it is doing anything with the rooms. I don't see them in the stage structure.
Make sure you're returning the updated version not the original version. Remember no mutation
Same gist link is updated
I think I am?
No I print them out and they are small and do not overlap
Actually if I change the last line to (do (println new-stage) new-stage)
I do see them added!
But this poses a problem
(add-rooms {:rooms []
:width 14
:height 20
:room-extent 4
:density 0.8})
{:rooms [#fizzbuzz.core.Room{:x1 8, :y1 14, :x2 11, :y2 17} #fizzbuzz.core.Room{:x1 5, :y1 16, :x2 8, :y2 19} #fizzbuzz.core.Room{:x1 4, :y1 8, :x2 7, :y2 11} #fizzbuzz.core.Room{:x1 7, :y1 4, :x2 10, :y2 8} #fizzbuzz.core.Room{:x1 1, :y1 7, :x2 4, :y2 11} #fizzbuzz.core.Room{:x1 1, :y1 3, :x2 4, :y2 7}], :width 14, :height 20, :room-extent 4, :density 0.8}
Ok actually this seems to work now. I updated my gist. The only thing left to do is do the carving in place-room
. That will take some thought to be immutable
I suppose I could push the whole project. It isn't very large.
One minute
@dpsutton Is boot instead of lein okay?
I don't have a lein project.clj
Oh ok. When you get that going you can eval (mfiano.crawler.stage/make-stage {:width 49 :height 49})
as an example. There are other parameters too, but that is the bare minimum
java.nio.file.NoSuchFileException: resources file: "resources" clojure.lang.ExceptionInfo: resources line: 5
I don't even have that file...
Can you pull the latest fix and retry?
Oh "resources" should be an empty directory in the root of the project. Seems boot creates that
I pushed that fix to the build file too
Everything should work for you now
Nice. So that will actually print the stage out too, which should be solid walls until those rooms are 'carved' out.
Hmm. What version of JVM and Clojure?
I'm not sure what ident is
JVM and CLJ are 1.9?
I'm on Clojure 1.9 and JVM 1.8 and I can't reproduce. That's ok. Take your time. I'll be here.
I also tried latest Clojure 1.10 alpha
I've been searching all over and I don't see where your error comes from. Do you have more info or were you able to get past it?
for some reason its cranking up into clojure 1.8 even if i specify 1.9. i don't know why
Oh I know why
Boot uses the Clojure version specified in your global properties file.
Not the one in build.boot
But yeah, that would be a problem considering spec was added in 1.9 ๐
you should have a boot.properties file in your dotfiles
Yeah @U04V70XH6 warned me about this when he got me to switch from lein. Only reason I remember ๐
So what i need to do is somehow, modify the cells of the room in place-room
.
Where that TODO is, I want to modify the features set of each cell of that room.
Sure. place-room is supposed to be iterating over all the cells of the room, and adding the keyword :room
to the :features #{}
set of each cell's hashmap
A cell is a hashmap, and a room contains many of these. I need to modify a particular key of each cell of the room to include this keyword in that set
ok. so we need to map over a list of rooms and update each's :features entry to include :room?
Not quite
ok. well the doseq doesn't seem right to me. we need something that we want to modify and we need to update it
Let me explain...
The stage
hashmap contains a :cells
key which is a vector of cells (also hashmaps themselves).
each cell object has a :features
key that is a set that needs a keyword added. The room just contains cells, but it's the stage which needs modifying
Does that make sense?
Ok. I'm not very familiar with reduce. It's rarely used in CL
(defn place-room [{:keys [width height rooms] :as stage} {:keys [x1 y1 x2 y2] :as room}] (let [updated-rooms (reduce (fn [acc room] ;;whatever ) [] rooms)]) (assoc stage :rooms updated-rooms))
No, CL has reduce
, but I've only used it like once in my 10 years of using that language exclusively ๐
it'll keep recursing with two arguments. the value you are building up and each entry in a collection
so we have all the rooms and we'll keep calling this function and build up the collection of rooms again
I'm trying to understand it ๐
Ok I see. There are 2 versions, with a starting value and not
So your example would be the same as (conj (conj (conj #{} :a) :b) :c)
So what should I reduce to?
]so we'll build up our rooms. starting with an empty vector [] and loop over all the rooms. and if we are interested we'll conj :room
into its features set
I guess I use update
in the reducing function to change the cell's features?
and we'll start with a collection of rooms and reduce it to a new collection of rooms
You know, I've tried to understand reduce a few times over the years, and your explanation just clicked
build up our rooms? don't you mean cells?
Also, what is the acc
param?
in the reduce above, it would be the empty set, the set containing just a and then the set containing a and b
I see. Let me try something and see if I understand
(reduce conj #{} [:a ๐ :c]) ;; => (conj #{} :a) -> #{:a} ;; => (conj #{:a} :b) -> #{:a :b} ;; => (conj #{:a :b} :c) -> #{:a :b}
I still need the doseq's though
I only have a room object. I need to get all that room object's cells
Yeah that changes things. I'm not sure how to do that. I have a single room that only has the 2 coordinates of a rectangle. I need to somehow loop to collect all of its cells before I can reduce
clojure works best when you think of how to make a new thing, rather than identify a few and modify them
At that time, no. place-room
only places a single room
But at that time the stage may not have them all
Remember that add-rooms
function you helped me with earlier? That is calling this function, place-room
, iteratively. This function is placing a single room into the stage.
and a room does not have a list of cells
it only has 2 points representing the rectangle in order to find its cells
So 1) All rooms are not in the stage in this function, except the last iteration, 2) I need to somehow collect the cells of the room that it's working on before I can reduce
That list is iteratively built BY this function
My whole point is that list doesn't exist until this function returns
and only for N rooms
Perhaps I'm not explaining myself well. Don;t worry about it ๐
I need to somehow collect the cells of the room that it's working on before I can reduce what does this mean
Ok are you looking at the file?
The last function in that file is recuring over place-room. place-room is updating the stage with a single room each recurrence. in place-room where you are telling me to get a list of all rooms, it doesn't have that information
Besides, it's not rooms that I need. I need a nested loop to collect the cells of a room.
ok. but remember, collecting things like that and then modifying them isn't a good way to work in clojure. because you can't mutate like that
so if you collect a subset, i'm not sure how you would recreate your original data structure. you cant just push or setq on them
All a room is is x1, y1, x2, y2. I need to loop over that range of cells in the grid, in order to get cells that should be updated in the actual stage
I don't want to mutate. I want to return a new data structure with the changes somehow
That's what placing a room involves. Finding the cells of a room, and then changing those cells features.
Yes the stage (grid) is the only thing ever being updated in this algorithm
The rooms are just static metadata
Ok, I made a little bit of progress, but I'm stuck so I'm open to that suggestion.
there's no need to render the room in the grid during this loop. just do them all after the room generation is done
and you can take the list of rooms and transform that list into a list of all of the grid coordinates
I made this but I wasn't expecting it to return a list of lists:
(defn collect-room-cells
[stage {:keys [x1 y1 x2 y2] :as room}]
(for [x (range x1 x2)]
(for [y (range y1 y2)]
(cell/get-cell stage x y))))
(let [{:keys [x1 y1 x2 y2]} (make-room {:width 64 :height 64 :room-extent 17})] (for [xcoord (range x1 y1) ycoord (range y1 y2)] [xcoord ycoord]))
(let [{:keys [x1 y1 x2 y2]} (make-room {:width 64 :height 64 :room-extent 17})]
(for [xcoord (range x1 y1)
ycoord (range y1 y2)]
[xcoord ycoord]))
so mapcat that over all of your rooms in grid creation and if that cell is in this list, mark it as room, else leave it empty
mapcat is a new one for me too. Going to have to look that up
turn that into a set for quick lookup. then on grid generation, if this "pixel" is in the list of room "pixels" mark it as such
This changes things. I have to re-read my CL code because it may happen that I don't even need to store the :rooms
in the stage hashmap
I can just mark the cells and throw the list away possibly
Ok I just looked over my old code and I am going to make some changes. The rooms shouldn't be stored in the stage. It's a temporary list.
I'm going to try doing what you suggested too. Thanks.
I spent all night trying to do what you said. I got farther. I generated the big list of cells like you said. And I did an attempt at deriving a new grid with feature set of some cells changed based on this list. It still has problems as noted in this comment. I'm out of ideas if you want to take a look: https://github.com/mfiano/crawler/blob/master/src/mfiano/crawler/room.clj#L63-L65
@dpsutton Thanks a lot for all your help. I think I am going to abandon this project. There must be something I'm not getting with Clojure to continue working with it.
i'm sure the issue of ending up with a list is easily solvable. probably conj-ing on a nil somewhere
I think a big problem is a chose a wrong data structure for the grid of cells
It's just a vector of cells in order. I don't know of a way to update only some of them and maintain the same order.
But I also expect there to be something rendered and there is not...it renders as all walls still.
So there must be something I don't get about clj
that's the paradigm shift you need to change. you can't change things. you need to have information and given this information, how do i make a new thing
so given all of the cells you have, given this grid, loop over all of its cells making a new grid. if the information tells you it should be a room, then mark the cell a room in the new collection
That's exactly what I did, but it's done wrongly because it conj's the new ones to the end, rather than putting them where they were.
carve-cells
so if you notice all of those "stage" parameters are the same initial value. stage does not change
yeah. i see. late night coding eror.
Also conj is not the right operation
I need to keep the order of the original cells
Ok, I see. That function purposefully is wrong in the sense that you pointed out. I wanted to just update the features of the original stage to see if something rendered, but it is still rendered as walls.
(defn make-grid [] (let [stage {:width 64 :height 64 :density 0.6 :room-extent 10} rooms (collect-rooms stage) coords (set (mapcat (fn [{:keys [x1 y1 x2 y2]}] (for [xcoord (range x1 x2) ycoord (range y1 y2)] [xcoord ycoord])) rooms))] (for [x (range (:width stage)) y (range (:height stage))] (cell/->Cell x y true 0 0 (if (contains? coords [x y]) #{:room} #{:wall})))))
I am going to need to do something better than hashset literals. There will be times when I want to add some instead of replace like that.
That's why I wrote the add-features and remove-features functions
Also there is a big problem with that code
The rooms need to be placed AFTER the original cells are created...not create the cells from the room cell coords. The next part of the algorithm is to carve a maze around all the rooms, and apply different features depending on each cell's neighbors. THis whole generator is highly mutable
I'm not sure if it can be done immutably...and surely not reversing the operations like you just did
(defn make-grid [] (let [stage {:width 8 :height 8 :density 0.6 :room-extent 4} grid (for [x (range (:width stage)) y (range (:height stage))] (cell/->Cell x y true 0 0 #{:wall})) rooms (collect-rooms stage) coords (set (mapcat (fn [{:keys [x1 y1 x2 y2]}] (for [xcoord (range x1 x2) ycoord (range y1 y2)] [xcoord ycoord])) rooms))] (reduce (fn [new-grid cell] (let [{:keys [x y]} cell] (conj new-grid (if (contains? coords [x y]) (-> cell (update :features conj :room) (update :features disj :wall)) cell)))) [] grid)))
so here we take an initial grid, and loop over it updating cells if they are in the room collection, otherwise leaving them alone
immutability can do everything that mutable can do. we just have to give instructions on how to create something new from something old
the idea is we loop over the grid with information about which ones should be marked as a room. and as we are looping, we just add in the original or add in a new cell that is modified how we want it to be modified
I see. If I use my add-features and remove-features functions here that use clojure.set functions for difference and union, will that work without converting the set into a list again? I sort of need this later on
Those functions are in cell.clj
At the moment I'm on a computer without my dev environment on a long car ride. When I get to my workstation later I'll try all your advice.
I just want to say again how much I appreciate your time and explanations
so change this (defn add-features [cell & features] (update cell :features clojure.set/union features))
(defn add-features [cell & features] (update cell :features clojure.set/union (set features)))
I see.
and the great thing about a repl and small functions like this is you should call them individually to make sure they work when using them in a big pipelin
if you give them malformed or non set type things they don't throw an error, they just do undefined behavior
I actually tested those functions in the repl and it didn't care they were lists...it was a set result
Oh undefined
(defn add-features [cell & features] (update cell :features clojure.set/union features))
(add-features {:x 1 :y 2 :features #{:a :b :c}} :d :e :f)
{:x 1, :y 2, :features #{:e :c :b :d :f :a}}
Why is that the case then?
merge
seems to be a subset of the functionality of into
per this example
cljs.user=> (merge {:a 1 :b 2 :c 3} {:b 9 :d 4})
{:a 1, :b 9, :c 3, :d 4}
cljs.user=> (into {:a 1 :b 2 :c 3} {:b 9 :d 4})
{:a 1, :b 9, :c 3, :d 4}
cljs.user=>
Is there some advantage to merge
that I am missing?also you can (merge nil a-map)
and get back a map, if you try that with into
you get a list
Hello! I am new here. So I just want to ask this. Can I consider clojure a general purpose language? Can you suggest a problem set? I plan to use clojure for engineering stuff but first of all I want to have fun doing cool algorithms.
At Cognitect we've used Clojure to implement games, large (and if I dare say) boring financial applications, lots of medical related stuff, imaging, educational software and more single page web apps than many programmers have had hot lunches. I also hear there is a database implemented in Clojure as well.
yes, it is a general purpose language and people use it for anything you can imagine
exercises: http://www.4clojure.com/ http://exercism.io/ http://adventofcode.com/ https://clojurecademy.com/
Fun quote from about 20 years ago from Kent Pitman, attempting to counter the impression that Common Lisp was a language specialized for Artificial Intelligence applications: "Please don't assume Lisp is only useful for Animation and Graphics, AI, Bioinformatics, B2B and Ecommerce, Data Mining, EDA/Semiconductor applications, Expert Systems, Finance, Intelligent Agents, Knowledge Management, Mechanical CAD, Modeling and Simulation, Natural Language, Optimization, Research, Risk Analysis, Scheduling, Telecom, and Web Authoring just because these are the only things they happened to list."
I don't know if Clojure has already been used for all of those application areas already, but it certainly could be.
Is there some Clojure web scraping, crawler framework can be suggested?
there are a couple linked at https://www.clojure-toolbox.com/ under โWebsite scrapingโ
Are sets implemented as linked lists?
Sets are not sequential... Check this https://clojure.org/guides/learn/hashed_colls
Right. I was wondering about their access time compared to lists and vectors for things like contains?
see https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/APersistentSet.java#L25-L27
Should have O(1) time, or is it log32(N) which quite close to constant? Or was that the vectors?
sets are hashed and stored in a tree. access time for lookup via contains? is O(log [base 32] n). A tree with 32-way branching factor is very shallow and wide, so in most cases you wonโt hit more than 1-2 branches).
Hi guys! Anyone here already used compojure-api?
@max.forasteiro ping. one of the authors here.
thank god! haha So, i have a end point that receives a json with a person and then I save this person to the population (that is a atom and the save is a swap!
to the atom)
idk if the person is not coerced or my return definition is wrong
my code:
(POST "/person/new" []
:return Person
:body [p Person]
:summary "Add person"
(ok (swap! persons #(conj % p))))
error:
{
"errors": "(not (map? a-job_queues.handler$add_agent$fn__15061))"
}
I think my mistake is on :return
definition, but idk if the p is a Hash after coercion
(POST "/person/new" []
:return Person
:body [p Person]
:summary "Add person"
(swap! persons conj p)
(ok p))
understood!
just one more question: can I put as much methods I want before send the ok
response?
Thanks!
in repl, you can call
(macroexpand-1 `(GET "/api" [] โฆ.))
to get the generated source code.At Cognitect we've used Clojure to implement games, large (and if I dare say) boring financial applications, lots of medical related stuff, imaging, educational software and more single page web apps than many programmers have had hot lunches. I also hear there is a database implemented in Clojure as well.
with a vector atom, how can I insert elements on the beginning and still has a vector?
(def var1 (atom []))
(swap! var1 conj var2) ;insert at the end and is a vector
(swap! var1 cons var2) ;serialize all contents of var2 to vector and converts to list
var2 is a hash
conj
inserts into a collection in an efficient way for the given collection. For lists, that means at the head, since the original list can just be pointed at in the tail of the new cons and the entire list does not have to be linearly searched in order to find the end. For vectors, this means growing it and inserting 1, instead of shifting the entire vector down one, also slow. For this you'd maybe want to take advantage of lazy sequences, and use (into [] ...)
.
test.core=> (def var1 (atom []))
#'test.core/var1
test.core=> (swap! var1 conj {:foo "bar"})
[{:foo "bar"}]
test.core=> (swap! var1 cons {:bar "foo"})
([{:foo "bar"}] [:bar "foo"])
the problem is: the cons call convert all to vector idk why
The cons function gives you back a sequence. Clojure has lots of functions like this that take a collection, turn it into a sequence and then do their thing, returning a sequence. Long story short, the vec function will take a sequence and turn it into a vector.
Oh and keep in mind that there are two completely separate things going on in your original example: The atom is just a mutable container for a value. I think the issue you are having is with vectors and sequences, where your vector happens to be stored in an atom.
Thank you @U9H7B8P7X! I've just realize that conj
and cons
params order are different lol
Oh yes, that! You're welcome.
I've been following the https://clojurescript.org/guides/quick-start and got stuck at the
clj --main cljs.main --compile hello-world.core --repl
step.
I'm getting
$ clj --main cljs.main --compile locau.core --repl
Error building classpath. Failed to read artifact descriptor for org.clojure:clojurescript:jar:1.10.238
org.eclipse.aether.resolution.ArtifactDescriptorException: Failed to read artifact descriptor for org.clojure:clojurescript:jar:1.10.238
at org.apache.maven.repository.internal.DefaultArtifactDescriptorReader.loadPom(DefaultArtifactDescriptorReader.java:276)
at org.apache.maven.repository.internal.DefaultArtifactDescriptorReader.readArtifactDescriptor(DefaultArtifactDescriptorReader.java:192)
โฆ
Caused by: org.eclipse.aether.resolution.ArtifactResolutionException: Could not transfer artifact org.clojure:clojurescript:pom:1.10.238 from/to central ( ): java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter mustbe non-empty
at org.eclipse.aether.internal.impl.DefaultArtifactResolver.resolve(DefaultArtifactResolver.java:422)
at org.eclipse.aether.internal.impl.DefaultArtifactResolver.resolveArtifacts(DefaultArtifactResolver.java:224)
Indeed, I don't see anything like org.clojure/clojurescript at https://repo1.maven.org/maven2/
What am I doing wrong?It's openjdk-9-jdk on Debian Stretch, from https://packages.debian.org/stretch-backports/
I have a web page to test with WebDriver. I figured I'd serve that page with ring, but I can't figure out a handler to write so that my app only serves that static resource. Like for instance, lein-ring requires that I have a handler in my profile, but the simplest handler instances have a body. How can I just serve a static page?
For the moment I'm having my WebDriver instance navigate to a file on my own filesystem. That doesn't seem right at all, though it will work as I develop other stuff.
This Slack is by far the friendliest programming community I've ever encountered in my many years coding. Not sure if this is Clojure in general, or Clojurians, but everyone here that has helped me over the last week or 2, I just want to say thank you. You know who you are. Hit my first milestone today thanks to a couple of you here. More learning tomorrow...