Fork me on GitHub
#beginners
<
2016-08-06
>
akiva01:08:52

(defn node-status [amx env & node-names]
  (do-node-status amx env (or node-names (list-nodes amx env))))

porglezomp02:08:26

How should I add an event listener while I’m writing reloading code?

thedavidmeister04:08:16

hi, how do i work with 64 bit unsigned integers?

thedavidmeister04:08:42

like, i have a few dumb questions about it πŸ˜›

thedavidmeister04:08:48

if i do (bit-shift-left 1 63)

thedavidmeister04:08:56

is that 1000000000000000000000000000000000000000000000000000000000000000 internally

thedavidmeister04:08:26

and how do i see that binary representation as a string

thedavidmeister05:08:19

i can’t quite get clojure.pprint/cl-format to do what I want 😞

thedavidmeister05:08:42

aaah i think i got it

mjhamrick18:08:12

I've been playing with implementing AVL trees in clojure and I wanted to write a property test to see if my balance is always within appropriate bounds after an insert. My avl tree is a nested map with :left and :right nodes. Is there a better way to do the collection of balance-factors than in this code I put together?

(defn balance-factors [avl-tree]
  (let [factors (atom [])]
    (w/postwalk (fn [thing]
                  (when (map? thing)
                    (swap! factors conj (balance-factor thing)))
                  thing) avl-tree)
    @factors))

mjhamrick18:08:06

I got it to work through a (very) recursive process also, but this did seem to be an easier way to collect what I was looking for. Thoughts?

senya2218:08:25

Trying to understand why `(defn task1 [ruleset inputs outputs ] (doseq [[left πŸ˜† right] ruleset] (do(conj inputs left) (conj outputs right))) [inputs outputs])` returns me an empty vector

senya2218:08:37

`(defn task1 [ruleset inputs outputs ] (doseq [[left πŸ˜† right] ruleset] (do(conj inputs left) (conj outputs right))) [inputs outputs])`

mjhamrick18:08:44

After looking more at my problem, I realized if I generalized the in-order traversal function I already made to take a function, I could just pass it balance-factor and it'll collect everything I need.

mjhamrick18:08:22

@senya22: Try surrounding your code by `

senya2218:08:34

Ah, thanks

senya2218:08:12

I want to iterate a collection and reshuffle its elements

senya2218:08:25

(defn task1 [ruleset inputs  outputs ]
  (doseq [[left :-> right] ruleset] (do(conj inputs left)
                                       (conj outputs right)))
  [inputs outputs])

senya2218:08:43

getting an empty vector somehow

senya2218:08:57

(def rule1 [[:a :b] :-> :c])
(def rule2 [[:c :d :e] :-> :f])

(def ruleset [rule1 rule2] )

senya2218:08:07

(task1 ruleset #{} #{})

mjhamrick18:08:49

One thing I see right away is that the

(do (conj inputs left)
(conj outputs right))
will only return the (conj outputs right) due to clojure's immutability.

senya2218:08:58

could you elaborate?

senya2218:08:42

basically I'd like to have a resulting structure with

[#{:a :b :c :d :e} #{:c :f}]

mjhamrick18:08:48

For clojure's do, it does all the code in the block, then only returns the last result. Usually you'd use a do block for side effects. conj doesn't cause any side-effects.

senya2218:08:23

probably do is not the right function here then...

senya2218:08:29

would there be a func of the box that does that kind of manipulation?

senya2218:08:11

out of the box

mjhamrick19:08:08

So this

(defn task1 [ruleset inputs outputs]
  (let [[[inputs-a _-> outputs-a]
         [inputs-b _-> outputs-b]] ruleset]
    [(set (concat inputs-a inputs-b))
     (set [outputs-a outputs-b])]))
Achieves the specific result you're asking for.

mjhamrick19:08:25

It just uses clojure's built in destructuring to break apart the ruleset that you pass into the function.

mjhamrick19:08:14

the _-> is just a symbol that's standing in for the :->. Underscores are what you'd generally use when you don't care about something in a destructure block, but you need to match on that 'place'

senya2219:08:51

indeed, but what if I extend my ruleset with another rule?

senya2219:08:23

def rule3 [[:k :d :e] :-> :m])
=> #'user/rule3
(def ruleset [rule1 rule2 rule3] )

senya2219:08:51

I'd like it to be able to iterate over a ruleset with multiple values in it

senya2219:08:50

and destructure while doing it

mjhamrick19:08:25

You can achieve what you're looking for by only destructuring the ruleset's one at a time with map.

senya2219:08:07

like what do you mean?

mjhamrick19:08:09

Sorry, it's hard to explain. I put together this example using your code.

(require '[clojure.set :as set])
(defn task1 [rulesets]
  (let [into-sets (map (fn [ruleset]
                         (let [[inputs _-> & outputs] ruleset]
                           [(set inputs)
                            (set outputs)])) rulesets)]
    (reduce (fn [[accumulated-input
                  accumulated-output]
                 [new-input
                  new-output]]
              [(set/union accumulated-input new-input)
               (set/union accumulated-output new-output)])
            into-sets)))
(def rule1 [[:a :b] :-> :c])
(def rule2 [[:c :d :e] :-> :f])
(def ruleset [rule1 rule2])
(task1 ruleset)

mjhamrick19:08:21

I broke up your task into 2 pieces. 1. Break out the inputs and outputs of each ruleset. 2. Combine these inputs and outputs using reduce. docs for map https://clojuredocs.org/clojure.core/map docs for reduce https://clojuredocs.org/clojure.core/reduce

senya2219:08:18

Thanks, let me wrap my head around it πŸ™‚

mjhamrick19:08:45

I updated the code a bit with better names. Hopefully that helps a bit!

senya2219:08:56

more than a little bit - I'm only like 1 week into clojure πŸ™‚

mjhamrick19:08:07

Good luck! I hope you enjoy the process. Functional concepts are a bit of a mind-bend if you come from the OO, or imperative world.

senya2219:08:20

yes, that's for sure...

senya2219:08:01

@mjhamrick: would you mind explaining once again this piece:

[[inputs _-> & outputs] ruleset]

senya2219:08:16

how does clojure think when it encounters the

[[:a :b] :-> :c]
and why the need for the &

mjhamrick19:08:22

That's clojure's destructuring at work. In some ways it's similar to pattern matching in other languages. The & is maybe not needed. It depends on how you consider outputs to be. In the case of clojure destructuring, an & will collect the rest of the values in a seq. By putting the & there, the outputs becomes a seq instead of a keyword. Since it's a seq, we can use the same pattern of (set my-seq). If we didn't use the &, we would need to use another option such as (into #{} output) or (#{output}) or (set [output]).

mjhamrick19:08:33

Try typing this into your repl.

(let [[inputs _-> outputs] [[:input-a :input-b] :-> :output]]
  (println inputs)
  (println _->)
  (println outputs))

(let [[inputs _-> & outputs] [[:input-a :input-b] :-> :output]]
  (println inputs)
  (println _->)
  (println outputs))

senya2219:08:26

I see, it treats it as a collection as if it were:

(let [[inputs _->  outputs] [[:input-a :input-b] :-> [:output-a :output-b]]]
  (println inputs)
  (println _->)
  (println outputs))
[:input-a :input-b]
:->
[:output-a :output-b]

mjhamrick20:08:04

It's worth noting that one of the important ways that destructuring is different from Pattern matching is that destructuring blows up when you throw something at it that can't match at all.

mjhamrick20:08:33

(let [[test] 3]
  test)

senya2220:08:14

I'm thinking of breaking up the rules into maps (instead of sets) where a key in a map would identify a particular rule and then the value would be what goes into the inputs and outputs sets now

dviramontes20:08:47

Hi all, (this is maybe an obvious concept but one that im failing to grasp) why does clojure’s map return a list sequence and not a vector in:

cljs.user=> (map #(+ 1 %) [1 2 3])
(2 3 4)

senya2220:08:55

@dviramontes: I think it returns a sequence

mjhamrick20:08:15

map always returns a seq. If you want to return a vector (sometimes you do) you can either call vec on the result, or use mapv instead of map.

dviramontes20:08:58

@senya22: thanks! @mjhamrick: i know i can do (mapv #(+ 1 %) [1 2 3]) but returning a seq is by design no? what is that design ? a transducer ? http://clojuredocs.org/clojure.core/map

dviramontes20:08:23

> Returns a transducer when no collection is provided.

dviramontes20:08:42

thats the part that confuses me..

mjhamrick20:08:21

I'm not sure on the exact reason, but I know one benefit of getting a lazy seq back from map is that you can do this.

(take 10 (map (partial + 1) (range))) ;; => (1 2 3 4 5 6 7 8 9 10)
(take 10 (mapv (partial + 1) (range))) ;; => runs forever

dviramontes20:08:10

interesting..

senya2220:08:04

@mjhamrick: trying to redefine your map-based rule parsing logic with

(defn map-rule-parser [rule]
      (let [[inputs _-> & outputs] rule]
           [(hash-map rule inputs)
            (hash-map rule outputs)]))


(defn task1 [rulesets]
      (let [into-sets (map map-rule-parser rulesets)]
           ;(println into-sets)
           (reduce (fn [[accumulated-input
                         accumulated-output]
                        [new-input
                         new-output]]
                       [(set/union accumulated-input new-input)
                        (set/union accumulated-output new-output)])
                   into-sets)))

senya2220:08:52

[{[[:a :b] :-> :c] [:a :b], [[:k :d :e] :-> :m] [:k :d :e], [[:c :d :e] :-> :f] [:c :d :e]}
 {[[:a :b] :-> :c] (:c), [[:k :d :e] :-> :m] (:m), [[:c :d :e] :-> :f] (:f)}]

senya2220:08:46

wondering if there's a better way to key by the rule name than above

mjhamrick20:08:47

You probably want to replace set/union with merge or merge-with (I can't remember which one)

senya2220:08:34

ah, ok - it seems to be still working though... or am I wrong?

codonnell20:08:09

@dviramontes: transducers give you a way of applying transformations to many other types of data structures, like streams, channels, strings (via StringBuilder), etc.

mjhamrick20:08:11

It looks like it is, but I think that's taking advantage of unspecified behavior of set/union. Since clojure sets are implemented as maps (citation needed) of value to value, union still works on them.

codonnell20:08:58

@mjhamrick: the behavior of set operations on non-set data structures is undefined; I wouldn't recommend using them on other data structures

senya2220:08:25

@mjhamrick: I'll look into the reducing behavior later, thanks

codonnell20:08:26

even if it works, it could potentially change in the future, and IIRC the set functions do unintuitive things to sequences

senya2220:08:25

@mjhamrick: but is this the best way to key by the rule name:

(let [[inputs _-> & outputs] rule]
    [(hash-map rule inputs)
     (hash-map rule outputs)]

senya2220:08:58

another words, how do I extract just the name out of that vector?

mjhamrick20:08:26

What do you mean by the name?

senya2220:08:03

I want the key to be something like: "[:a :b] :-> :c]"

mjhamrick20:08:54

Isn't that what the keys is now? Or do you actually want it as a string?

mjhamrick20:08:42

You can already use (get 'result-of-your-function-here' [[:a :b] :-> :c]) to get values out

senya2220:08:46

yes. would it be better as a string or perhaps as a keyword, if I later want to use functions on that map to extract pieces

senya2220:08:28

but with a keyword I could do (:name map), not?

mjhamrick20:08:32

To turn into a string, you'll probably want to look into clojure.string/join, and the str function from core.

senya2220:08:48

great, thanks

mjhamrick20:08:05

Yes. But keep in mind there isn't any problem with using (get my-map my-key)

mjhamrick20:08:23

So as long as the keys are good in your domain, feel free to use any composite clojure value as the key.

senya2220:08:20

Thanks for the explanation @mjhamrick !

senya2221:08:03

and @codonnell πŸ™‚

senya2221:08:42

@mjhamrick: would you mind explaining how that reducing function works once again please? Specifically, what are its parameters, also destructuring at work?

senya2221:08:13

(defn set-rule-parser [rule]
      (let [[inputs _-> & outputs] rule]
           [(set inputs)
            (set outputs)]))




(defn task1 [rulesets]
      (let [into-sets (map set-rule-parser rulesets)]
           (println "into-sets: " into-sets)
           (reduce (fn [[accumulated-input
                         accumulated-output]
                        [new-input
                         new-output]]
                       [(set/union accumulated-input new-input)
                        (set/union accumulated-output new-output)])
                   into-sets)))

mjhamrick21:08:28

That reduce is a bit complicated since it's using quite a few features of clojure all at once. I'll try to break it down into the pieces.

senya2221:08:50

yes, I'm trying to split it into multiple pieces

senya2221:08:54

(defn reduction [[accumulated-input
      accumulated-output]
     [new-input
      new-output]]
  [(set/union accumulated-input new-input)
   (set/union accumulated-output new-output)])
=> #'user/reduction
(reduce reduction [#{:b :a} #{:c}] [#{:e :c :d} #{:f}] [#{:e :k :d} #{:m}])
ArityException Wrong number of args (4) passed to: core/reduce  clojure.lang.AFn.throwArity (AFn.java:429)

mjhamrick21:08:41

;; The reduce is taking a function of 2 arguments. The first argument is the
;; collector, and the second argument is a single item.

;; The function to the reduce is taking advantage of the fact that Clojure lets
;; you destruct directly from the arguments vector.
(fn [[a b] [c d]]
  ;; do something
  )
;; is basically equivalent to
(fn [a-and-b c-and-d]
  (let [[a b] a-and-b
        [c d] c-and-d]
    ;; do something    
    ))
;; Keeping that in mind, we can see that the reducing function we're using is
;; breaking apart the accumulator and the current item. We're accumulating with
;; input and output as separate sets or maps. Each item that's passed in is also
;; a vector of 2 items. The input and the output from a single function. This
;; came in through mapping the set-rule-parser. So each input to the reduction
;; function is a into-sets item.

senya2222:08:11

I was trying to reproduce it given that into-sets: ([#{:b :a} #{:c}] [#{:e :c :d} #{:f}] [#{:e :k :d} #{:m}]), but getting that ArityException

senya2222:08:09

(reduce reduction (seq [#{:b :a} #{:c}] [#{:e :c :d} #{:f}] [#{:e :k :d} #{:m}])) ArityException Wrong number of args (3) passed to: core/seq--4128 clojure.lang.AFn.throwArity (AFn.java:429)

mjhamrick22:08:41

Oh I see what the issue was. You're giving 4 things to reduce where it's expecting one collection (in this case of the 4 things)

mjhamrick22:08:12

(reduce reduction [[#{:b :a} #{:c}] [#{:e :c :d} #{:f}] [#{:e :k :d} #{:m}]]) slight change. Just added brackets

senya2222:08:33

hm, I attempted this before, thought it was the same thing:

ArityException Wrong number of args (2) passed to: PersistentVector  clojure.lang.AFn.throwArity (AFn.java:429)
(reduce reduction (seq [#{:b :a} #{:c}] [#{:e :c :d} #{:f}] [#{:e :k :d} #{:m}]))

senya2222:08:59

sorry:

(reduce reduction (seq [#{:b :a} #{:c}] [#{:e :c :d} #{:f}] [#{:e :k :d} #{:m}]))
ArityException Wrong number of args (3) passed to: core/seq--4128  clojure.lang.AFn.throwArity (AFn.java:429)

senya2222:08:41

I guess it still needs an enclosing collection...

senya2222:08:45

So, in a reducing function, is it always the case that the first arg is an accumulator and a second is a current value? Would that be a correct assumption? And then you simply taking advantage of a fact that every incoming item is a composition of 2 things: inputs & output

mjhamrick22:08:48

In a general reducing function you are right. Accumulator is the first value, and the second is a value from the collection you are reducing over.

mjhamrick22:08:04

Here is a really simple example. (reduce + [1 2 3 4])

mjhamrick22:08:08

One small 'catch' of reduce, is that if you don't give it an initialization value, it will take the first two items from your collection. In this example, it takes 1 to be the accumulator and 2 to be the item from the collection. These are the arguments to +, and then the result (3) is the new accumulator for the next value (3).

mjhamrick22:08:27

This continues

acc = 1 -> 3 -> 6 -> 10
itm = 2 -> 3 -> 4
where there are no items left, reduce returns the accumulator.

senya2222:08:52

I see. It becomes more obvious when one rights his own reducing function though, not that obvious from an out-of-the-box '+' or '*' functions

mjhamrick22:08:13

I agree. I think it's easiest to understand once you have written your own reducing functions. Once you get used to it, though, it becomes a very powerful abstraction for taking a sequence of values and reducing them to a single value.