Fork me on GitHub
#beginners
<
2020-10-29
>
Jim Newton07:10:24

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.

Jim Newton07:10:01

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)))))

Ben Sless09:10:18

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?

Jim Newton09:10:38

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

Ben Sless10:10:41

Makes sense. I wonder if it can be expressed as a transducer, then you won't have an issue with laziness / next

yuhan10:10:06

@U010VP3UY9X note that your seq argument is shadowing the clojure.core function, it's more idiomatic to name it coll

yuhan10:10:56

(defn lazy-pairs [coll]
  (if-let [[x & xs] (seq coll)]
    (concat (for [x' xs]
              [x x'])
      (lazy-pairs xs))
    ()))

Jim Newton11:10:58

what does if-let do when you bind multiple variables?

Ben Sless12:10:07

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

Ben Sless12:10:31

You can find online custom versions of if-let which support multiple bindings, usually called if-let*

jkida09:10:38

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)])))))))

jkida09:10:12

Is loop/recur what i should be reaching for here?

mbjarland11:10:37

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.

Eamonn Sullivan12:10:19

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?

ghadi13:10:15

@eamonn.sullivan see the function attached to this ticket https://clojure.atlassian.net/browse/CLJ-2555 for a useful way to do this

Malik Kennedy16:10:48

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)

Roger Amorin Vieira18:10:13

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"]

Ben Sless18:10:24

Vectors accumulate at the end, lists accumulate at the beginning. You can see this difference with conj, too

practicalli_john20:10:16

into is basically reduce conj

phronmophobic18:10:13

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

hiredman18:10:29

always supply an initial value to reduce is a good habit to get in to

6
Roger Amorin Vieira19:10:39

Thanks, for all help

Rafał Wyszomirski21:10:44

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?

practicalli_john11:10:37

If you are okay with online books, then there are several I am working on at https://practicalli.github.io/

👍 1
alexmiller21:10:44

the Clojure books on Pragmatic are great; you should buy them all

alexmiller21:10:17

(shh, no one tell him I co-authored two of them)

😄 4
Rafał Wyszomirski21:10:56

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 🙂

alexmiller21:10:58

definitely a lot of updates between 1st and 3rd editions. I myself learned Clojure from the 1st edition!

alexmiller21:10:43

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

Rafał Wyszomirski21:10:52

Great news, thank you. I must say they're pretty hard to get in print unfortunately

alexmiller21:10:35

they make great gifts, I recommend buying 10 copies of each

🎄 3
🎁 3
😆 5
seancorfield21:10:34

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 🙂

seancorfield21:10:23

@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.

Rafał Wyszomirski22:10:27

I like Manning quite a lot so I think I will take a look at Joy of Clojure. Thanks for all the help 🙂

seancorfield22:10:51

JoC is very much the "why" of Clojure. It's a good second book on Clojure.

👍 1
seancorfield21:10:45

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).

alexmiller22:10:58

I think most of it is now pretty dated unfortunately. I've been contemplating how to make that idea live again

seancorfield22:10:49

I would be happy to contribute updated material for the JDBC stuff (I helped out with the original JDBC content).

Rafał Wyszomirski22:10:11

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)

Rafał Wyszomirski22:10:23

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 :)

seancorfield23:10:44

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

seancorfield23:10:06

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.

seancorfield23:10:38

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 🙂

seancorfield23:10:20

(there are also quite a few "local" channels which can be good places to find additional resources -- #clojure-poland assuming you're from there?)

Rafał Wyszomirski07:10:53

Yup :) thank you for all the answers 👍 As you said, that should keep me busy. Have a good day 🙂