This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-10-29
Channels
- # architecture (3)
- # aws-lambda (1)
- # babashka (7)
- # beginners (60)
- # calva (33)
- # chlorine-clover (8)
- # cider (24)
- # circleci (4)
- # clj-kondo (13)
- # cljs-dev (1)
- # cljsrn (12)
- # clojure (76)
- # clojure-australia (6)
- # clojure-europe (100)
- # clojure-france (1)
- # clojure-nl (13)
- # clojure-uk (16)
- # clojurescript (49)
- # conjure (1)
- # cryogen (8)
- # datomic (43)
- # dirac (3)
- # etaoin (1)
- # exercism (4)
- # fulcro (32)
- # jobs (2)
- # kaocha (4)
- # nginx (1)
- # off-topic (106)
- # pathom (8)
- # reagent (5)
- # reitit (5)
- # sci (52)
- # shadow-cljs (37)
- # tools-deps (30)
- # tree-sitter (18)
- # xtdb (18)
what is a best way to generate a lazy sequence of ordered pairs from a given sequence? The output sequence can be in any order, I don't care. For example given (1 2 3 4)
, generate the sequence [[1 2] [1 3] [1 4] [2 3] [2 4] [3 4]]
which does not include [1 1]
(repeated elemente) unless 1 occurs twice in the input sequence, and does not include [2 1]
because 2 does not precede 1 in the input sequence.
Here's what I'm using, but I don't like calling rest
on something that might be an array.
(defn lazy-pairs [seq]
(cond (empty? seq)
()
:else
(concat (for [t (rest seq)]
[(first seq) t])
(lazy-pairs (rest seq)))))
Another option is using next
, but it's not much better. Here's what I came up with
(defn lazy-pairs
([coll]
(lazy-seq
(when-let [s (seq coll)]
(let [[h & t] coll]
(concat
(map #(list h %) t)
(lazy-pairs t)))))))
Why are you worried about it being an array? Why does it have to be lazy?the reason I was trying to make it lazy was that the list might be long sometimes. and if i'm using something like (some pred (lazy-pairs ...))
i'd like that to stop generating when it finds a match
Makes sense. I wonder if it can be expressed as a transducer, then you won't have an issue with laziness / next
@U010VP3UY9X note that your seq
argument is shadowing the clojure.core function, it's more idiomatic to name it coll
(defn lazy-pairs [coll]
(if-let [[x & xs] (seq coll)]
(concat (for [x' xs]
[x x'])
(lazy-pairs xs))
()))
what does if-let do when you bind multiple variables?
if-let supports only one binding form, which can contain destructuring, i.e.
(if-let [[x & xs] ,,,)
is valid, but (if-let [x ,,, y ,,,] ,,,)
is invalid
You can find online custom versions of if-let which support multiple bindings, usually called if-let*
Is there a (better) alternative using some higher order functions to avoid using loop/recur for the following:
(defn demo-loop-recur [n]
"update n maps of an ordered vec where (:changeable? m),
when changeable? then update (assoc m :did-change? true)
else
(assoc m :did-change? false)
"
(let [demo-vec [{:id 1 :changeable? true}
{:id 2 :changeable? false}
{:id 3 :changeable? true}
{:id 4 :changeable? false}
{:id 5 :changeable? true}
{:id 6 :changeable? true}
{:id 7 :changeable? true}]]
(loop [remain-coll demo-vec
change-count 0
result []]
(if (empty? remain-coll)
result
(let [[m & remaining] remain-coll
should-change (and (< change-count n)
(:changeable? m))
new-cnt (if should-change
(inc change-count)
change-count)]
(recur remaining
new-cnt
(into result [(assoc m :did-change? should-change)])))))))
Generic question. Assume I have a reasonably complex xml file and I want to edit it in clojure (i.e. parse into data, make some edits, emit back to xml). The editing consists of finding a number of nodes using some predicates and changing their content in some way, perhaps adding a new child node, changing an attribute etc. I am more or less familiar with zippers and say the xml->
function for selecting data from a document, but have failed to find a clean way of doing a multi node edit. It seems to me that xml->
does not immediately play nice with zip/edit
as you would need to essentially reduce over the to-be-edited nodes/locs located by xml->
and I haven't found a clean way to do this. Then again, I might just be missing something obvious.
I can of course treat the parsed xml structure as data and start doing update-in
etc, but that gets very chatty and I'm hoping there is a cleaner/more concise/more idiomatic way. Any help much appreciated.
(defn reducer [acc curr]
(let [{:keys [changes-remaining result]} acc]
(if (and (> changes-remaining 0)
(:changeable? curr))
(assoc (update acc :result conj (assoc curr :did-change true)) :changes-remaining (dec (:changes-remaining acc)))
(assoc (update acc :result conj (assoc curr :did-change false)) :changes-remaining (:changes-remaining acc)))))
(let [demo-vec [{:id 1 :changeable? true}
{:id 2 :changeable? false}
{:id 3 :changeable? true}
{:id 4 :changeable? false}
{:id 5 :changeable? true}
{:id 6 :changeable? true}
{:id 7 :changeable? true}]]
(:result (reduce reducer {:changes-remaining 5 :result []} demo-vec)))
;=> [{:id 1, :changeable? true, :did-change true}
{:id 2, :changeable? false, :did-change false}
{:id 3, :changeable? true, :did-change true}
{:id 4, :changeable? false, :did-change false}
{:id 5, :changeable? true, :did-change true}
{:id 6, :changeable? true, :did-change true}
{:id 7, :changeable? true, :did-change true}]
(:result (reduce reducer {:changes-remaining 1 :result []} demo-vec))
;=> [{:id 1, :changeable? true, :did-change true}
{:id 2, :changeable? false, :did-change false}
{:id 3, :changeable? true, :did-change false}
{:id 4, :changeable? false, :did-change false}
{:id 5, :changeable? true, :did-change false}
{:id 6, :changeable? true, :did-change false}
{:id 7, :changeable? true, :did-change false}]
Id be interested if someone with more experience (I'm pretty much a beginner so my idea might be completely wrong nonsense) can show a way to do reduce where you need to keep track of multiple things, in this instance change-count and the result ?Maybe something along the lines of
(defn change-first-n [n xs]
(let [remaining (volatile! n)]
(mapv (fn [x]
(assoc x :did-change (or (not (pos? (vswap! remaining dec)))
(:changeable? x))))
xs)))
local mutability that does not escape the scope if fine, you have to be careful of course…
Here is another version i came up with.. i dont think its better then the reduce version though.
(defn demo-mapped-index [n]
"Alternative Option
First track vec by idx and set everything to did-change? false
Then filter by changeable and take n.
Using the idx tracked from step1 update specific items.
"
(let [demo-vec [{:id 1 :changeable? true}
{:id 2 :changeable? false}
{:id 3 :changeable? true}
{:id 4 :changeable? false}
{:id 5 :changeable? true}
{:id 6 :changeable? true}
{:id 7 :changeable? true}]]
(let [idx-demo-vec (map-indexed (fn [idx itm] (assoc itm :idx idx :did-change? false)) demo-vec)
filter-n (take n (filter #(:changeable? %) idx-demo-vec))]
(apply assoc (vec idx-demo-vec) (mapcat (fn [x] [(:idx x) (assoc x :did-change? true)]) filter-n)))))
Hi everyone. I'm in the middle of refactoring an older bit of code (that I wrote when first learning clojure) and I have a view instances of patterns like this:
(defn get-page-of-stuff
[access-token org topics page-size cursor]
(core/make-graphql-post
access-token
(core/get-graphql "search-query")
{:first page-size :query (get-query org topics) :after cursor}))
(defn get-all-pages
[access-token org topics page-size]
(let [page (get-page-of-stuff access-token org topics page-size nil)]
(loop [page page
result []]
(let [pageInfo (-> page :data :search :pageInfo)
has-next (pageInfo :hasNextPage)
cursor (pageInfo :endCursor)
result (concat result (get-nodes page))]
(if-not has-next
(into [] result)
(recur (get-page-of-stuff access-token org topics page-size cursor)
(get-nodes page)))))))
I seem to remember that this loop/recur thing is quite low-level and there might be a better, more functional approach. Basically, I get a page of results and a pageInfo
object that has a hasNextPage
property. If true, I get the next page, if not, I'm done. How would someone with more of a clue than me do this?@eamonn.sullivan see the function attached to this ticket https://clojure.atlassian.net/browse/CLJ-2555 for a useful way to do this
the docstring is a bit complicated but the step!
argument is a function that gets the initial or next page
Nice. Thanks!
Many, many thanks for this. I learned quite a bit. I drastically dumbed it down to my (basic) level, because I have a very specific use case (paging through Github API searches), but it still meant I could consolidate several specific functions into a general, reusable one. I can replace my iterate-pages with the real core/iteration when 1.11 comes out.
(defn iterate-pages
"Iterate through the pages of a Github GraphQL search.
pager -- cursor -> page function to get a page of results.
results? -- page -> boolean function that returns true if the page contains values.
vf -- page -> values function that extracts the values from a page.
kf -- page -> cursor function that extracts the cursor for the next page, or nil if there isn't one."
[pager results? vf kf]
(reify
clojure.lang.Seqable
(seq [_]
((fn next [ret]
(when (results? ret)
(concat (vf ret)
(when-some [k (kf ret)]
(lazy-seq (next (pager k)))))))
(pager nil)))))
(defn get-all-pages
"Convenience function for getting all of the results from a paged search.
getter -- function that returns a single page, given a cursor string.
results? -- function that returns a boolean indicate whether the page contains values.
valuesfn -- function to extract the values from a page."
[getter results? valuesfn]
(let [get-next (fn [ret] (if (-> ret :data :search :pageInfo :hasNextPage)
(-> ret :data :search :pageInfo :endCursor)
nil))]
(into [] (map identity (iterate-pages getter results? valuesfn get-next)))))
An example use:
(defn get-page-of-repos
[access-token org topics page-size cursor]
(core/make-graphql-post
access-token
(core/get-graphql "repo-search-query")
{:first page-size :query (get-query org topics) :after cursor}))
(defn get-repos
"Get information about repos in a given organisation, with the specified topics"
([access-token org topics] (get-repos access-token org topics *default-page-size*))
([access-token org topics page-size]
(let [get-page (partial get-page-of-repos access-token org topics page-size)
results? (fn [page] (some? (get-nodes page)))]
(core/get-all-pages get-page results? get-nodes))))
Ah, yes, sorry. I didn't copy that right. The original code used concat.
In fact, I'm beginning to think the original code might have been fine. I can use into [] there and I don't think I can do that with yours.
I did eventually figure it out and use cons
instead, which gets me a lazy sequence of pages (seq of seqs) instead of a individual search results. What I meant about the original code is that this is for a simple CLI that produces a JSON file on disk. I'm not processing them one at a time or something, where the lazy sequence would make a lot more sense. But perhaps I should be... :thinking_face: Always learning! Thanks for the help.
What am I missing to get a secure (as-in CSRF) POST input form (using reitit / luminus?) #+FILE: guestbook/routes/home.clj
(defn chat-page [request]
(layout/render request "chat.html"
{:messages (db/get-all-messages)}))
(defn save-message [message]
(db/create-message! message)
(response/found "/chat"))
(defn home-routes []
[""
{:middleware [middleware/wrap-csrf
middleware/wrap-formats]}
["/" {:get home-page}]
["/about" {:get about-page}]
["/chat"
{:get chat-page}]
["/chat/:content"
{:post save-message}]
])
#+FILE: guestbook/resources/html/chat.html
<form action="/chat" method="POST">
{% csrf-field %}
<textarea id="content" rows="3"></textarea>
<button>submit</button>
</form>
<ul>
{% for message in messages %}
<li> {{ message }} </li>
{% endfor %}
</ul>
#+FILE: guestbook/resources/sql/queries.sql
-- :name get-all-messages :? :*
-- :doc retrieves all messages records
SELECT * FROM chat
-- :name create-message! :! :n
-- :doc creates a new message record
INSERT INTO chat
(content)
VALUES (:content)
Someone can give me an explain why the both did not give the same result?
(reduce into '(("a" "b" "c") ("d" "e" "f")))
; ("c" "b" "a" "a" "b" "c")
(reduce into [["a" "b" "c"] ["a" "b" "c"]])
; ["a" "b" "c" "a" "b" "c"]
Vectors accumulate at the end, lists accumulate at the beginning. You can see this difference with conj, too
into
is basically reduce conj
into
uses conj
.
For lists
> https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/conj puts the item at the front of the list.
For vectors
> https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/conj puts the item at the end of the vector
Since no initial value is provided to reduce:
> If [an initial] val is not supplied, returns the result of applying f to the first 2 items in coll
see https://clojure.org/reference/data_structures#Lists
Thanks, for all help
Good evening. Can you recommend some good books about Clojure? I like Pragmatic Bookshelf books and I have always considered them of high quality. What do you think?
If you are okay with online books, then there are several I am working on at https://practicalli.github.io/
the Clojure books on Pragmatic are great; you should buy them all
I have the first edition of "Programming Clojure" but I think I am more interested in web development. The third edition looks great though, I will wait for the sale you have mentioned 🙂
definitely a lot of updates between 1st and 3rd editions. I myself learned Clojure from the 1st edition!
if you are willing to wait a bit, they will be having their yearly 40% off sale on e-books over Thanksgiving in a few weeks
Great news, thank you. I must say they're pretty hard to get in print unfortunately
they make great gifts, I recommend buying 10 copies of each
Joking from the author aside, I have the following Pragmatic books on Clojure: Clojure Applied, Getting Clojure, Programming Clojure (both the 2nd Ed and the 3rd Ed), and Web Development with Clojure (3rd Ed) -- and they're all really good. I also have Programming Concurrency on the JVM and Seven Languages in Seven Weeks, which both feature some Clojure too 🙂
@rawyszo I hear good things about Living Clojure as a beginner's book (O'Reilly). I learned Clojure mostly from Joy of Clojure (Manning) and Clojure Programming (O'Reilly) but that was a long time ago.
I like Manning quite a lot so I think I will take a look at Joy of Clojure. Thanks for all the help 🙂
O'Reilly's "Clojure Cookbook" is a good, practical book too (although it's from 2014 so some of its content may be a bit dated now, and some of the libraries it discusses have been superseded by better choices).
I think most of it is now pretty dated unfortunately. I've been contemplating how to make that idea live again
I would be happy to contribute updated material for the JDBC stuff (I helped out with the original JDBC content).
Fun fact - I've a actually switched to Emacs and started learning Clojure because of Clojure for the Brave and True (the book is hilarious btw, love it)
Anyway, thank you for all the suggestions. 🙏 I also wanted to ask if there are some more general resources about dealing with the whole env/ecosystem? More specifically I'm talking about configuration, architecture, tools etc. Or maybe some useful articles about leiningen, shadow-cljs and figwheel would be great. There's always googling things but it would be nice to have some good examples :)
Hard to say in general @rawyszo. The http://clojure.org site has information about the Clojure CLI and deps.edn
which you'll see in some newer books and tutorials: https://clojure.org/guides/deps_and_cli
For architecture, Clojure Applied covers that to some degree as I recall, but you'll find folks aren't very proscriptive in Clojure, in general. There's some stuff out there about Domain-Driven Design in Clojure. The most general advice you'll get is to separate the pure functions from the side-effecting functions -- "functional core, imperative shell" is another approach you'll hear people talk about.
For Leiningen, Shadow, Figwheel, etc -- I'd say their respective websites are the definitive documentation. There are lots of channels here when you need to dig deeper on stuff: #tools-deps (for deps/CLI) #leiningen #figwheel-main #figwheel #lein-figwheel #shadow-cljs #architecture ... that should keep you busy for a while 🙂
(there are also quite a few "local" channels which can be good places to find additional resources -- #clojure-poland assuming you're from there?)
Yup :) thank you for all the answers 👍 As you said, that should keep me busy. Have a good day 🙂