Fork me on GitHub
#beginners
<
2020-07-08
>
chucklehead00:07:46

for clojure.data.xml it seems like the alias-uri mappings only apply when emitting xml, is there something similar for the inverse? i.e. when parsing instead of getting #...Element{:tag :xmlns.abcxyz/tagName} I could get #...Element{:tag ::my-alias/tagName}?

Frosku00:07:20

Easiest way to dockerize a Clojure application?

Frosku00:07:27

I have a k8s instance already

ghadi00:07:34

make an uberjar, put it into a java container running java -jar $yourjar.jar as the docker CMD

ghadi00:07:23

no need to use "clojure" docker images

ghadi00:07:42

any of the adoptopenjdk images will suffice

Chase00:07:05

For this 4Clojure problem interleaving two sequences http://www.4clojure.com/problem/39 I came up with:

(fn [coll-1 coll-2]                                                         
  (flatten (into [] (zipmap coll-1 coll-2))))
Which works in my editor/repl for all the tests but fails on the first test in 4clojure with a "Failed the unit tests" error. Can't figure out what I'm doing wrong.

noisesmith00:07:05

4clojure uses an old version of clojure (1.4 or 1.5)

Chase00:07:38

I looked at the source of all 3 core functions and the latest was flatten added with 1.2

ghadi01:07:01

doesn't it tell you which unit test failed?

noisesmith01:07:13

btw the puzzle can be solved with two functions and no parens

Chase01:07:45

It fails right on that first one. I copied over the unit tests to my editor and all come out true

ghadi01:07:09

yeah that breaks on clojure 1.4

ghadi01:07:22

clj -Sdeps '{:deps {org.clojure/clojure {:mvn/version "1.4.0"}}}'

ghadi01:07:26

to try it ^

ghadi01:07:37

user=> (def x (fn [coll-1 coll-2]
  (flatten (into [] (zipmap coll-1 coll-2)))))
#'user/x
user=> (= (x [1 2 3] [:a :b :c]) '(1 :a 2 :b 3 :c))
false
user=> (x [1 2 3] [:a :b :c])
(3 :c 2 :b 1 :a)

Chase01:07:37

Oh, that is a neat way to try on old versions. Ok.

ghadi01:07:51

4clojure is bizarre

noisesmith01:07:20

oh I see what broke

ghadi01:07:24

good problems, but the execution infra is old

ghadi01:07:57

maybe @U04V15CAJ has a good way to sandbox code for a fresher 4clojure experience

borkdude05:07:57

babashka is already used for 4clojure problems in the shell: https://github.com/porkostomus/4bb sci (the interpreter on which babashka builds) also has sandboxing options

noisesmith01:07:27

the hash map has no defined order

Chase01:07:59

Yeah if I reverse the sequence before passing it to flatten it all passes

noisesmith01:07:12

btw flatten is terrible

noisesmith01:07:20

it makes me flinch when I see it :D

Chase01:07:45

How do you approach it?

practicalli-johnny12:07:16

I have a collection of videos that walk through how I though about and solved in different ways the first 60 challenges on 4Clojure. I encourage you to try the relevant challenges before looking at the video that covers those challenges 🙂 https://www.youtube.com/playlist?list=PLpr9V-R8ZxiDB_KGrbliCsCUrmcBvdW16

Chase16:07:12

Nice! I have about 50 solved so far so I'll start checking these out

ghadi01:07:59

to elaborate a bit, flatten can shred through multiple levels of things

ghadi01:07:19

and people abuse it to achieve a single-level splice

noisesmith01:07:44

@chase-lambert due to 4clojure's substitution model, this works mapcat list

Chase01:07:07

Yup, I'm reading that in the saved solutions now. You are one of my saved people btw

noisesmith01:07:49

yeah, for a single level, you usually want concat (or something similar like cat or mapcat )

ghadi01:07:26

(map vector [a b] [1 2]) -> ([a 1] [b 2]) -> (apply concat that) (a 1 b 2)

ghadi01:07:20

you can leverage the fact that map can take multiple collections

ghadi01:07:38

and apply a function "slicing" an item from all the collections in parallel

Igor Lenterman01:07:43

Hey guys! Been reading about Clojure a lot online and have decided to invest the time and effort in learning. Anywhere you would recommend starting? What is the fastest way to learn Clojure? What is the most enjoyable way to learn Clojure?

practicalli-johnny12:07:03

You may find the books and video guides I created useful https://practicalli.github.io/ I recommend finding a Clojure aware editor you like using. I use Spacemacs but there are several common editors that support Clojure http://practicalli.github.io/clojure/clojure-editors/ I learned a lot about the core functions in clojure by doing http://4Clojure.com challenges. There is a playlist of videos about that on the practicalli website. There are lots of tutorials on the internet on how to build things in Clojure. I am adding more to the books over the coming months, so let me know if you are looking for something specific. Good luck.

👍 6
Stuart08:07:35

@U05254DQM, a video series on connecting to a db (SQL or Postgress) in a clojure'y way, doing selects, inserts etc would be great! I'm finding it hard to find good sources on this.

practicalli-johnny09:07:48

@U013YN3T4DA Thanks for the suggestion, it's a very useful topic so I'll move it up my (very long) Todo list. I have some basic content in the Practicalli webapps book, but it's very simplistic and needs updating. Many people use SQL fairly directly so next.jdbc seems a great project to use and I'll probably pick Sean Cornfield's brain on this. https://www.hugsql.org/ also seems a nice tool. I also like to use migrations with SQL too.

practicalli-johnny09:07:30

Take a look at the https://cljdoc.org/d/seancorfield/next.jdbc/1.1.547/doc/getting-started docs, the look very useful and have many examples

Stuart10:07:35

Thanks for these links! I'll have a read. THis has been a big stumbling block for me in Clojure, I think not being familiar with java doesn't help It's frustrating as in C# (my day job) accessing a db is really simple. I've noticed that with Clojure, it can take stuff that is quite hard in other language and make it so simple, yet some of the stuff that is simple that I don't have to think about in C# seem quite hard. Guess that will evaporate over time as I get more familiar with Clojure and what the popular / default packages are.

Stuart10:07:15

I see you are doing a series on CI, I think that will be very helpful!

ghadi01:07:43

namely, vector

Chase01:07:54

Sounds good! Thank you

Alex Miller (Clojure team)01:07:29

@igor.lenterman there truly is no one answer to any of those questions - everyone will find different material to connect with

Alex Miller (Clojure team)01:07:46

many people like Clojure for the Brave and True as a fun intro

Igor Lenterman01:07:32

any projects you would recommend a beginner to practice building?

Alex Miller (Clojure team)01:07:33

it's free online or you can buy it in paper form

Igor Lenterman01:07:42

I prefer building to reading for learning code

Alex Miller (Clojure team)01:07:13

simple games are often a good starting point for me - rock paper scissors, etc

borkdude05:07:57

babashka is already used for 4clojure problems in the shell: https://github.com/porkostomus/4bb sci (the interpreter on which babashka builds) also has sandboxing options

borkdude05:07:44

Also, unrelated to babashka/sci, but related to 4clojure: I do have a slightly updated version of clojail here: https://github.com/borkdude/clojail

Stuart09:07:44

@igor.lenterman, there is also codewars. https://www.codewars.com/kata/search/clojure?q=&amp;r[]=-8&amp;beta=false You can specify just to show kata that have clojure solutions. A nice thing here is once you solve the problem in clojure you get to see other peoples solutions and some are voted on best practice so you can often learn new techniques just by looking at others solutions.

Igor Lenterman17:07:03

Thank you! Excited to jump in

practicalli-johnny07:07:50

Codewars has a lot of mathematics oriented challenges, where you need to be able to recognise the maths techniques from the minimal description. There is also https://exercism.io/tracks/clojure which feels like a wider range of challenges. And http://4clojure.com helps you learn the clojure.core functions.

Stuart09:07:18

You can start off at 8 KYU (the easiest puzzles) and work your way up to 1 KYU, some of which can be very hard puzzles

xconconx18:07:21

(defn my-reduce
  ([f initial coll]
   (loop [result initial
          remaining coll]
     (if (empty? remaining)
       result
       (recur (f result (first remaining)) (rest remaining)))))
  ([f [head & tail]] --> ?
   (my-reduce f head tail)))
I'm having a little bit of trouble understanding the line marked with a question mark here. Anyone have any insight? I know that we start with a function definition which takes parameters f initial and coll, then we initialize a loop with result bound to initial and remaining bound to coll, if what's remaining in our collection is empty then return the result else we recur using the result of applying the function to the initial value and the first item of what's remaining? But, the lines after are a little confusing

noisesmith18:07:09

(defn ([x] ...) ([x y] ...)) defines a function with two bodies, and one is picked based on the count of the argument list

noisesmith18:07:46

so we have here, if we ignoring destructuring: (defn ([f init coll] ...) ([f init-and-coll] ...))

noisesmith18:07:12

then the destructuring [head & tail] uses one element as the "init" and the ones after as the coll

noisesmith18:07:15

by a self call

noisesmith18:07:18

hope that helps

xconconx18:07:24

So the first time we execute the second block to set init and tail and the first block handles breaking down until remaining is empty?

noisesmith18:07:19

right - we call what you call "blocks" the bodies of the function

xconconx18:07:40

ahhh ok i see

noisesmith18:07:57

first clojure invokes the body matching the count of the arg list, then it can self-call (that's the most common pattern in multi-arity code)

xconconx18:07:09

yes i forgot all about the arity stuff

xconconx18:07:19

ok it makes more sense now

noisesmith18:07:28

sometimes functions have totally different code is invoked (see map for example) but that's more rare

👍 3
naxels19:07:55

Hi guys, here is a question that’s been bugging me for 3 weeks.. I’ve parsed the output generated by tree (with json output) and here is what the datastructure looks like: a Vector containing 1-n Maps:

[{:type "directory",
  :name ".",
  :contents
  [{:type "directory",
    :name "Intro",
    :contents
    [{:type "file", :name "01 What The Program is About and How to Do It.mp4"}
     {:type "file", :name "02 How Training Works 5PS.mp4"}]}
   {:type "directory",
    :name "Session 01 - Fundamentals",
    :contents
    [{:type "directory",
      :name "01 Prepare",
      :contents
      [{:type "file", :name "01 Standing Knee to Chest.mp4"} {:type "file", :name "02 Single Leg Deadlift.mp4"}]}
     {:type "directory",
      :name "02 Practice",
      :contents
      [{:type "file", :name "01 General Kneeling Lunge.mp4"} {:type "file", :name "02 Modified Pigeon Stretch.mp4"}]}
     {:type "file", :name "Let's Get This Started.mp4"}]}]}]
I know this is a tree structure and I need some kind of recursion to parse it

naxels19:07:12

ultimately i want to generate html using hiccup from this, but i can’t seem to ‘crack’ the puzzle

naxels19:07:41

i’ve try’d many things, the latest:

(defn process-tree [vec-with-maps]
  (map (fn [{:keys [type name contents]}]
         (cond
           (= "directory" type) (do
                                  (println (str "dir:" name))
                                  (when contents
                                    (process-tree contents)))
           (= "file" type) (println (str "file: " name))))
       vec-with-maps))

(process-tree json-parsed)

naxels19:07:58

the problem with this besides the side effects

naxels19:07:27

is that it first does the directories before the files in the :contents

naxels19:07:03

Should I go at this completely differently and use something like a Walk or Zip

naxels19:07:15

or am I just not ‘getting’ recursion?

noisesmith19:07:32

a recursive function that eagerly walks to the bottom, then builds a data structure from the bottom up should work

noisesmith19:07:43

as you go down, keep a stack of each level you visited

noisesmith19:07:19

as you go up, return a "clojurization" of the item you are visiting to your parent, using the clojurized results your children passed back via that stack

naxels19:07:45

do you know of an example where i could sneak peak at, as this makes sense, i just have no clue how to do it

naxels19:07:21

sounds like I would reduce the tree structure into some other type of data structure and then ‘step through’ it?

noisesmith19:07:27

what I'm talking about is how to make that data structure

noisesmith19:07:42

if all you want is to process it for side effects, use tree-seq

noisesmith19:07:20

(tree-seq :contents seq ...) - that will give a lazy-seq of every node

noisesmith19:07:43

then you can map across the top level of each node, knowing you'll only hit each one once

naxels20:07:11

that looks interesting

noisesmith20:07:12

@patrick.glind

(ins)user=> (map :name (tree-seq :contents :contents dirs))
(nil)
(ins)user=> (map :name (tree-seq :contents :contents (first dirs)))
("." "Intro" "01 What The Program is About and How to Do It.mp4" "02 How Training Works 5PS.mp4" "Session 01 - Fundamentals" "01 Prepare" "01 Standing K
nee to Chest.mp4" "02 Single Leg Deadlift.mp4" "02 Practice" "01 General Kneeling Lunge.mp4" "02 Modified Pigeon Stretch.mp4" "Let's Get This 
4")
(ins)user=> (pprint *1)
("."
 "Intro"
 "01 What The Program is About and How to Do It.mp4"
 "02 How Training Works 5PS.mp4"
 "Session 01 - Fundamentals"
 "01 Prepare"
 "01 Standing Knee to Chest.mp4"
 "02 Single Leg Deadlift.mp4"
 "02 Practice"
 "01 General Kneeling Lunge.mp4"
 "02 Modified Pigeon Stretch.mp4"
 "Let's Get This Started.mp4")
nil

noisesmith20:07:20

(where dirs is literally what you pasted)

noisesmith20:07:30

I think that hit everything correctly, you should double check of course

naxels20:07:36

this is great stuff and so much easier 🙂

noisesmith20:07:25

that's a common experience with clojure - the right function turns your problem into a non-problem

noisesmith20:07:45

the data structure / data manipulation libs just do most of the work

naxels20:07:51

now i just want to generate h1 for directories and ul/li for files with hiccup and i’m done

noisesmith20:07:34

nice, so you just replace (map :name ...) with a a map of some function looking at type plus name and returning an h1...

noisesmith20:07:37

sounds simple

noisesmith20:07:42

you'd want to modify that code slightly if you ever had more than one hash-map in that top level vector

noisesmith20:07:25

maybe it would be better to construct {:contents dirs} instead of calling on (first dirs) - that less likely to break in the future while offering the same behavior

naxels20:07:59

this is a sample of the full directory layout

naxels20:07:36

it does still start with the (first dirs)

naxels20:07:10

i got 1 part working ha:

(defn directory-to-html [name]
  (html [:h1 name]))

(map #(directory-to-html (:name %)) (tree-seq :contents :contents (first dirs)))

naxels20:07:12

i think i got it:

(defn directory_or_folder [type name]
  (cond
    (= "directory" type) (directory-to-html name)
    (= "file" type) (file-to-html name)))

(map #(directory_or_folder (:type %) (:name %)) (tree-seq :contents :contents (first dirs)))

naxels20:07:28

with helper functions:

(defn directory-to-html [name]
  (html [:h1 name]))

(defn file-to-html [filename]
  (html [:li filename]))

naxels20:07:08

1 thing that is ‘wrong’ is that the sublevel is not really ‘saved’

noisesmith20:07:11

right - there's nothing that tracks that in the current code

naxels20:07:52

would it be too difficult to fix this?

naxels20:07:06

maybe somehow sort the data structure so that files come before directories?

noisesmith20:07:31

right - replace the second :contents with a sort - that might suffice

noisesmith20:07:52

err, with something that accesses :contents but sorts files first that is

noisesmith20:07:58

but if different nestings carry different meanings, you'll need something else I think - maybe something tricky where the second instance of :contents (that is the function that returns the children) also looks at the current level, and increments the level of each child...

naxels20:07:08

i will try, i think for now I will keep it simple

noisesmith20:07:29

(defn children-of
  [{:keys [level contents] :as el}]
  (->> contents
       (sort-by :type)
       (map (fn [c] (assoc c :level (inc level))))))
#'user/children-of
(ins)user=> (map (juxt :name :type :level) (tree-seq :contents children-of {:contents dirs :level 0}))
([nil nil 0] ["." "directory" 1] ["Intro" "directory" 2] ["01 What The Program is About and How to Do It.mp4" "file" 3] ["02 How Training Works 5PS.mp4"
 "file" 3] ["Session 01 - Fundamentals" "directory" 2] ["01 Prepare" "directory" 3] ["01 Standing Knee to Chest.mp4" "file" 4] ["02 Single Leg Deadlift.
mp4" "file" 4] ["02 Practice" "directory" 3] ["01 General Kneeling Lunge.mp4" "file" 4] ["02 Modified Pigeon Stretch.mp4" "file" 4] ["Let's Get This Sta
rted.mp4" "file" 3])
(ins)user=> (pprint *1)
([nil nil 0]
 ["." "directory" 1]
 ["Intro" "directory" 2]
 ["01 What The Program is About and How to Do It.mp4" "file" 3]
 ["02 How Training Works 5PS.mp4" "file" 3]
 ["Session 01 - Fundamentals" "directory" 2]
 ["01 Prepare" "directory" 3]
 ["01 Standing Knee to Chest.mp4" "file" 4]
 ["02 Single Leg Deadlift.mp4" "file" 4]
 ["02 Practice" "directory" 3]
 ["01 General Kneeling Lunge.mp4" "file" 4]
 ["02 Modified Pigeon Stretch.mp4" "file" 4]
 ["Let's Get This Started.mp4" "file" 3])
nil
one stab at it, it's an interesting problem

noisesmith20:07:08

all directories are before sibling files so the sort is opposite of what you want

noisesmith20:07:26

but see how it accurately shows levels at least

noisesmith20:07:46

by mapping a function that adds 1 to the level of the parent

naxels20:07:47

wow that is awesome

noisesmith20:07:33

you could use a similar approach to give each one an href link back to its parent, or to make an adjacency list that allows reconstructing the full form (with optional cycles) out of the "flattened" form here

noisesmith20:07:48

all those things require is a unique id generator

noisesmith20:07:00

(and a slight modification to the map fn of course)

naxels20:07:53

thank you so much for your help

naxels20:07:57

i’m a beginner at this

naxels20:07:06

and this gets me started big time 🙂

Pavel Klavík20:07:49

while using tree-seq seems nice (I didn't even knew it existed), I would also recommend as a learning exercise to do it without it, so you know how to write this using plain recursion

naxels20:07:39

How would you approach this @pavel.klavik as I was thinking the same thing after first getting a basic version working

naxels20:07:19

i’m a beginner and i tried various approaches i got from books/internet but nothing got me actually there while it seems so simple

naxels20:07:47

Here’s how I drew the diagram:

noisesmith21:07:32

that diagram accurately describes how to descend, but doesn't describe how to create a result as you do so

Pavel Klavík21:07:15

I would start with thinking how it would work outside Clojure. What should each step do? If I assume that I already have a step as a black box, how their answers can be composed.