This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-08-07
Channels
- # admin-announcements (3)
- # beginners (133)
- # boot (123)
- # capetown (2)
- # cider (21)
- # cljs-dev (1)
- # cljsrn (2)
- # clojure (32)
- # clojure-czech (1)
- # clojure-russia (2)
- # clojure-spec (72)
- # clojurescript (4)
- # datomic (28)
- # emacs (19)
- # funcool (1)
- # hoplon (10)
- # jobs-rus (1)
- # onyx (1)
- # other-languages (3)
- # protorepl (3)
- # re-frame (10)
- # reagent (76)
- # ring (2)
- # rum (2)
- # spirituality-ethics (61)
- # test-check (2)
- # testing (1)
- # untangled (3)
I'd like to express it as a dependency graph via (require '[com.stuartsierra.dependency :as dep])
kind of like
(def graph1 (-> (dep/graph)
(dep/depend :f :e)
(dep/depend :f :c)
(dep/depend :f :d)))
(defn build-rule-tree [graph rule]
(let [[lhs rhs] rule]
;(prn lhs :-> rhs)
(map #(-> graph (dep/depend rhs %)) lhs)
))
is there a way to keep a global graph object as I need to walk a set of vectors, say
([#{:b :a} #{:c}] [#{:e :c :d} #{:f}] [#{:e :k :d} #{:m}] [#{:t :p} #{:k}])
eventually to build the whole graphIsn’t that what reduce
is for?
(reduce build-rule-tree dep/graph ([#{:b :a} #{:c}] [#{:e :c :d} #{:f}] …))
Unless I’m misunderstanding something
Perhaps... I'm trying to implement something similar since, but getting exceptions:
(def rule1 [[:a :b] :-> :c])
(def rule2 [[:c :d :e] :-> :f])
(def rule3 [[:k :d :e] :-> :m])
(def rule4 [[:p :t] :-> :k])
(def ruleset [rule1 rule2 rule3 rule4] )
(defn set-rule-parser [rule]
(let [[inputs _-> & outputs] rule]
[(set inputs)
(set outputs)]))
(defn break-out-rules [ruleset]
(map set-rule-parser ruleset))
(defn task2 [ruleset]
(let [into-sets (break-out-rules ruleset)]
(println "into-sets: " into-sets)
(reduce graph-building-reducer dep/graph into-sets)
))
(defn graph-building-reducer [[lhs rhs]]
(map #(dep/depend rhs lhs) lhs)
)
(defn graph-building-reducer [[lhs rhs]]
(map #(dep/depend rhs lhs) lhs)
)
CompilerException java.lang.IllegalArgumentException: No single method: depend of interface: com.stuartsierra.dependency.DependencyGraphUpdate found for function: depend of protocol: DependencyGraphUpdate, compiling:(C:\Users\Simeon\AppData\Local\Temp\form-init1745806617618307544.clj:3:9)
Not sure what's wrong. And after changing:
(defn graph-building-reducer [[lhs rhs]]
(map #(prn rhs "->" lhs) lhs)
)
it complains about something else: (task2 ruleset)
IllegalStateException Attempting to call unbound fn: #'task2.core/task2 clojure.lang.Var$Unbound.throwArity (Var.java:43)
@senya22: you might want to look at the functions in walk
: https://clojure.github.io/clojure/clojure.walk-api.html
You can also do extremely powerful data structure building & transforming with clojure.zip
(https://clojure.github.io/clojure/clojure.zip-api.html) but it’s a steeper learning curve & quite possibly overkill for what you’re doing.
There’s also the 3rd-party specter
lib (https://github.com/nathanmarz/specter), which is a really powerful way to do nested data structure transforms.
That’s just with a very quick look at what you’re doing; I could be misunderstanding what you’re after.
Alternately, if you just want to express rules and check data against them, without explicitly building a dependency graph, core.logic
is a fantastic tool for that. That’s not the way you’ve expressed the problem, but depending on what you’re going to use the dependency graph for, it may be another approach.
Briefly - I'm writing a function that performs basic inference on rule systems with the rules expressed as shown above. It should take two arguments: a set of rules and a set of input assertions
And you want to check to see whether the assertions are valid given the set of rules? core.logic
may be the ideal tool for that, then.
At this stage, the function it should return the set of all outputs that the rule system can derive from those inputs
At a later stage, this function is going to be modified to take a set of rules as its only argument, uses them to create a pretreated data structure somehow, and returns a new function as its result. This resulting function should take a set of input assertions as its only argument, and it should return the set of valid inferences with the aid of what was computed in the pretreatment step.
Yeah, I’d take a look at core.logic
for that. In some complex cases (eg if you may have rules that result in infinite loops, say if you had rules :a -> :b
and :b -> :a
, then you might need to consider a rules engine. But them’s the big guns 😉
Glancing back, I see you’ve already been going with the current approach for a while — don’t let me deter you from that! Depends, I guess, whether you’re doing it as a learning exercise or you just want results 😄. With your current approach, walk
and specter
seem likely to come in handy.
You might also want to consider using core.match
to express your ruleset. https://github.com/clojure/core.match
That's why I tried this reducing approach - would like to figure why the graph-building-reducer
breaks
I’m not familiar enough w/ Sierra’s dependencies lib to have any sense of why you’re getting that error.
In general, the error you’re getting (as of 12:17) means that dep/depend
is a protocol function, but doesn’t have a matching arity. You’re calling it with the args rhs
and lhs
— what args does its documentation say it wants?
I tried eliminating that library and it threw the other error that I listed (as of 12:36) - I'm suspecting it's my syntax that is wrong
Hmm, at a glance it does look like it has a 2-arg arity, based on the examples in the readme.
Although in the code it looks like it wants 3:
(defprotocol DependencyGraphUpdate
(depend [graph node dep]
(from https://github.com/stuartsierra/dependency/blob/master/src/com/stuartsierra/dependency.cljc )I tried the example from that site, it works fine:
(def g1 (-> (dep/graph)
(dep/depend :b :a) ; "B depends on A"
(dep/depend :c :b) ; "C depends on B"
(dep/depend :c :a) ; "C depends on A"
(dep/depend :d :c))) ; "D depends on C"
Ah, but in that case, the 1st argument is being inserted by the threading macro (`->`).
ie (-> (dep/graph) (dep/depend :b :a))
is equivalent to (dep/depend (dep/graph) :b :a)
Yes, looks like we can do that through a reduce
as per @porglezomp 's suggestion
Maybe instead of (map #(dep/depend rhs lhs) lhs)
you meant (map #(dep/depend rhs %) lhs)
?
But then you still need to have a way to insert the graph
arg in there. Or did you perhaps mean (map #(dep/depend % rhs lhs) lhs)
? That only makes sense if you’re treating the lhs as the graph.
well, the into-sets
is of this form:
into-sets: ([#{:b :a} #{:c}] [#{:e :c :d} #{:f}] [#{:e :k :d} #{:m}] [#{:t :p} #{:k}])
so I want to say that the second(rhs) set of each vector depends on every element in the lhs set
As for inserting graph, I'm not sure whether I need to pass it in a call to reduce, or should it be mentioned as a first arg in the reducing function
(map somefn lhs)
means that you want to apply somefn
to each member of lhs
. Is that what you're meaning to do? Or is it that you want to apply it to each element in the into-sets
list?
Let me write up a snippet here...
(defn graph-building-reducer [graph [lhs rhs]]
;(map #(prn rhs "->" lhs) lhs)
(map #(dep/depend graph rhs %) lhs)
)
this doesn't complain, however it does when I'm trying to invoke:
=> #'task2.core/graph-building-reducer
(task2 ruleset)
IllegalStateException Attempting to call unbound fn: #'task2.core/task2 clojure.lang.Var$Unbound.throwArity (Var.java:43)
OK, I'm gonna have to take a more thorough look at the code and get a more general sense of what's going on. (unless @porglezomp clears it up for you in the meantime)
Will it always be [#{:b :a} #{:c}]
, or might it be [#{:b :a} #{:c :d}]
?
If it’s the former why not write them as [#{:b :a} :c]
?
I’m just asking so I can make the write assumptions in this snippet here.
Hm, I thought about it. It seems that the consequence of the rule is always a singular outcome the way the problem is stated
So the set-rule-parser
handles creating of the vector of sets - something that was built in a previous step.
Okay, so you’re being handed this set by something else. I’ll work for a set of one element only?
That previous step also used to reduce the resulting structure into-set
, but differently, but I think you were right by suggesting that reduce could be used here as well - that's why I went this route
(def rule1 [[:a :b] :-> :c])
(def rule2 [[:c :d :e] :-> :f])
(def rule3 [[:k :d :e] :-> :m])
(def rule4 [[:p :t] :-> :k])
(def ruleset [rule1 rule2 rule3 rule4] )
(defn task2 [ruleset]
(let [into-sets (break-out-rules ruleset)]
(println "into-sets: " into-sets)
(reduce graph-building-reducer dep/graph into-sets)
))
(defn graph-building-reducer [graph [lhs rhs]]
;(map #(prn rhs "->" lhs) lhs)
(map #(dep/depend graph rhs %) lhs)
)
(defn set-rule-parser [rule]
(let [[inputs _-> & outputs] rule]
[(set inputs)
(set outputs)]))
And before, here's how the reducing was done, since we needed just 2 resulting collections - all inputs and all outputs
;;defs
(def rule1 [[:a :b] :-> :c])
(def rule2 [[:c :d :e] :-> :f])
(def rule3 [[:k :d :e] :-> :m])
(def rule4 [[:p :t] :-> :k])
(def ruleset [rule1 rule2 rule3 rule4] )
;;task 1
(require '[clojure.set :as set])
(defn set-rule-parser [rule]
(let [[inputs _-> & outputs] rule]
[(set inputs)
(set outputs)]))
(defn reducer [[accumulated-input
accumulated-output]
[new-input
new-output]]
[(set/union accumulated-input new-input)
(set/union accumulated-output new-output)])
(defn break-out-rules [ruleset]
(map set-rule-parser ruleset))
(defn task1 [ruleset]
(let [into-sets (break-out-rules ruleset)]
(println "into-sets: " into-sets)
(reduce reducer into-sets)
))
(task1 ruleset)
;into-sets: ([#{:b :a} #{:c}] [#{:e :c :d} #{:f}] [#{:e :k :d} #{:m}] [#{:t :p} #{:k}])
;=> [#{:e :k :c :b :d :t :p :a} #{:m :k :c :f}]
Mine works on the original ruleset, does this do what you want?
@porglezomp may have a complete solution for you; I'm just pointing out what was wrong with the code you had written. I'll turn it over to y'all (but feel free to tag me w/ questions).
I don’t have much more to say than what I have there, I’m a beginner to Clojure but a veteran at using reduce
.
(failed to notice you still had the map in graph-builder-reducer
& made a mess of things). Here's what I think you meant to do:
(defn graph-building-reducer [graph [lhs rhs]]
(dep/depend graph rhs lhs))
(defn task2 [ruleset]
(let [into-sets (break-out-rules ruleset)]
(println "into-sets: " into-sets)
(reduce graph-building-reducer (dep/graph) into-sets)))
@porglezomp: Mindboggling - I need to try to see what it returns 🙂
That gives the following result (let me know if it matches your intention):
into-sets: ([#{:b :a} #{:c}] [#{:e :c :d} #{:f}] [#{:e :k :d} #{:m}] [#{:t :p} #{:k}])
{:dependencies {#{:c} #{#{:b :a}}, #{:f} #{#{:e :c :d}}, #{:m} #{#{:e :k :d}}, #{:k} #{#{:t :p}}},
:dependents {#{:b :a} #{#{:c}}, #{:e :c :d} #{#{:f}}, #{:e :k :d} #{#{:m}}, #{:t :p} #{#{:k}}}}
dep/depend needs to be run on each element respectively though, doesn’t it? Not (dep/depend graph #{:a :b} #{:c})
but (-> graph (dep/depend :c :a) (dep/depend :c :b))
Tried @eggsyntax's snippet - seems to be right, at the same time @porglezomp is right, it needs to thread onto each element
Does mine give the result you expect?
Yeah, I was missing that inner "loop". I suspect you could do the whole thing fairly elegantly with a nested for
, but I'd have to mess with it.
(reduce graph-building-reducer graph ruleset)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: graph in this context, compiling:(C:\Users\Simeon\AppData\Local\Temp\form-init1745806617618307544.clj:1:1)
@porglezomp: or should it be part of the task2
function?
Oh, right, that should be dep/graph
I think?
@porglezomp: not sure
The intention is that the symbol that I use to have as graph
there should be the initial graph that you want to add to.
@eggsyntax: but somehow the graph seems right from its toString(). Weird...
@senya22: if the result is the one @eggsyntax posted, it looks wrong from here, since it has things like sets of symbols depending on sets of sets, when you probably want single symbols depending on sets of symbols.
@porglezomp: ah, yes - sets of sets seem wrong
@porglezomp: are you able to run your snippet? It throws that exception for me...
I haven’t set up a project with dependency
, but I can do that if you’d like me to fiddle with it.
But really, just change graph
into whatever graph you want to start with, which might be (dep/graph)
or something.
The graph
in the reduce
that’s not working for you. The initial value.
@senya22: want to post a snippet of the complete code as it stands? That'll help. I can also help w/ debugging it.
I changed the snipped I posted.
IllegalArgumentException No implementation of method: :depend of protocol: #'com.stuartsierra.dependency/DependencyGraphUpdate found for class: clojure.lang.Keyword clojure.core/-cache-protocol-fn (core_deftype.clj:554)
Oh, I figured it out, I think
OK, here's the snippet with @eggsyntax's suggestions incorporated:
I fixed the snippet, it produces:
#com.stuartsierra.dependency.MapDependencyGraph{:dependencies {:c #{:b :a}, :f #{:e :c :d}, :m #{:e :k :d}, :k #{:t :p}}, :dependents {:a #{:c}, :b #{:c}, :c #{:f}, :d #{:m :f}, :e #{:m :f}, :k #{:m}, :p #{:k}, :t #{:k}}}
Does the fixed one look like what you need? I had left the initial value of the reducing function as the wrong object.
Happy to help! Nice work @porglezomp, esp if you don't have much clj experience yet 🙂
I’ve done some lisp before, and I’ve used a lot of different programming languages, so picking up one more has gone pretty quick. @senya22 I can give a quick explanation of what my code’s doing.
@porglezomp: please - greatly appreciated
How familiar are you with reduce
? You seemed to be getting it mixed up with map
earlier, so how much detail about it should I give?
The idea we’re working with here is that we need to apply dep/depend
to the graph a lot of times, with different arguments. If we do (dep/depend somegraph :a :b)
and then (dep/depend somegraph :a :c)
, it’s going to produce two completely different graphs, the second one doesn’t include the first.
So we need to do (dep/depend (dep/depend somegraph :a :b) :a :c)
, and that will add both.
(reduce f xs)
will give us (f (f (f (f x1) x2) x3) x4)
etc,
Yep, immutability at work.
So we take reduce
, and pass it an empty graph as its accumulator
And then we walk over the list of rules, and apply one rule at a time, making the new graph the new value of the accumulator
graph-building-reducer
should really be called apply-rule
.
So I’ll refer to it as that. In apply-rule
, we do a similar kind of reduce over the first element of [[:a :c :c] :-> :d]
. We take some graph
, and we apply (dep/depend graph :d :a)
, etc.
So the call (reduce #(dep/depend %1 target %2) graph srcs)
will produce something like (dep/depend (dep/depend (dep/depend graph :d :a) :d :b) :d :c)
.
hey guys, any example writting unit tests that uses mock data instead of the database? (it would be great if I can see how you test queries and insert/update methods)