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?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)])))))))
Is loop/recur what i should be reaching for here?
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
@jimka.issy 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))
()))
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 ?what does if-let do when you bind multiple variables?
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?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…
thanks, I hadn't come across (volatile!) before 🙂
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*
@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
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))))
Don't do recursive concat
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.
Nice. Thanks!
Ask me anything about it and I’ll try to help async
Interesting, thanks guys.
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
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
always supply an initial value to reduce is a good habit to get in to
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)))))
Thanks, for all help
into
is basically reduce conj
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
(shh, no one tell him I co-authored two of them)
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
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!
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.
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 like Manning quite a lot so I think I will take a look at Joy of Clojure. Thanks for all the help 🙂
I think most of it is now pretty dated unfortunately. I've been contemplating how to make that idea live again
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)
JoC is very much the "why" of Clojure. It's a good second book on Clojure.
I would be happy to contribute updated material for the JDBC stuff (I helped out with the original JDBC content).
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 🙂