Fork me on GitHub
#beginners
<
2016-08-15
>
senya2200:08:13

Need help extending the following code to accommodate some extra logic which will affect the result. Here's the current code:

(let [facts #{:a :b :d :e}
      dependencies {:m #{:e :k :d}, :c #{:b :a}, :f #{:e :c :d}}]
  (map first (filter (comp #(set/subset? % facts) last) dependencies )))
=> (:c)

senya2201:08:49

What I want is that when the subset finds a match, the corresponding key (in this case :c) needs to be added onto the facts and the whole mapping phase will need to re-occur, thus resulting in (:c :f) output. How would I express such state change in code? Thanks!

madstap01:08:23

Are you sure you're not missing a :c in facts?

senya2201:08:07

That's the whole point - I want to add it in dynamically

senya2201:08:07

Does it make sense?

senya2201:08:19

so basically I want - when the subset function returns 'yes' for its check to stick the :c onto the facts set which will need to make the filtering/mapping to repeat at which point the #{:e :c :d} will also get satisfied (and the :f will be added to the result (as well is to the facts)

senya2201:08:00

basically I want a mechanism to keep updating the state of the facts until the final solution is reached

madstap01:08:45

Something like this?

(first                                      ; At the end, get acc from the vector

   (reduce (fn [[acc facts] [k v]]            ; Each pass through, k and v will be a key and value from the map
             (if (set/subset? v facts)        ; If v is a subset of facts
               [(conj acc k) (conj facts k)]  ; Add k to both acc and facts
               [acc facts]))                  ; Else pass them through unchanged

           ;; Initial values of acc(umulator) and facts
           [[] #{:a :b :d :e}]
           {:m #{:e :k :d}, :c #{:b :a}, :f #{:e :c :d}}))

senya2202:08:46

yes, that does it - interesting how you enclosed the initial accumulator and the facts into an vector(for ease of destructuring, I guess?). I changed the initial acc to a #{} as a set is expected.

madstap02:08:14

Yeah, the acc can be changed to a set if that's what you need. It'll work the same.

madstap02:08:15

Just beware that maps aren't ordered. So you're not guaranteed to always get the same answer

senya2202:08:17

that's true, maybe I need to change the dependencies into a sorted map or something?

madstap02:08:49

Yeah, that would do it

madstap02:08:52

Or a seq of key-value tuples, if you want it ordered in some way other than sorted.

senya2202:08:44

(into (sorted-map) dependencies)for the first case, right?

madstap02:08:02

Yup, exactly

senya2202:08:28

great, thanks a lot

senya2202:08:36

ah, maybe I was to quick - it still doesn't re-attempt the evaluation as apparent by reordering values in dependencies map:

(first                                      ; At the end, get acc from the vector


  (reduce (fn [[acc facts] [k v]]            ; Each pass through, k and v will be a key and value from the map
            (if (set/subset? v facts)        ; If v is a subset of facts
              [(conj acc k) (conj facts k)]  ; Add k to both acc and facts
              [acc facts]))                  ; Else pass them through unchanged

          ;; Initial values of acc(umulator) and facts
          [[] #{:a :b :d :e}]
          {:m #{:e :k :d}, :f #{:e :c :d}, :c #{:b :a} }))
;;=> [:c]

senya2202:08:14

somehow I want to react to a state change in facts and restart the reduce operation once such change detected. Would it be 'atom' I'll need to use, or something to that effect?

madstap13:08:21

Yeah, you could use an atom for that. There's a function add-watch that you can use to set up a callback on an atom, to be run when the atom is changed.

madstap13:08:10

@senya22: When iterating through dependencies, if the value is a subset of facts, you want that key to be added to facts. Also, keys can be added to facts by some other means. And every time facts has changed, you want to iterate through dependencies again, because there might be new values that are now subsets of facts. Am I understanding correctly?

senya2214:08:09

yes, I want the reducing operation to react to the changes in the facts state until all the elements in dependencies map are exhausted, irrespective of the elements order there

senya2214:08:57

@madstap: When you say: "Also, keys can be added to facts by some other means." - this is currently not required maybe in the future. Facts are given as an argument to the function and could be updated by it internally. Also retracting the facts from that set is not in scope so far, however making it reactive to any kind of change will be better than otherwise, I'd think

senya2214:08:54

That's why I wasn't sure how to express something like that in idiomatic Clojure code

saicheong14:08:21

@seancorfield: Thanks for the link to java.jdbc documents.

madstap14:08:34

@senya22: Does this satisfy the requirements? I wrapped the reduce in a loop recur to re-run if the facts were updated. Now the order of the hash is irrelevant.

(defn f [facts deps]
  (loop [acc #{}
         facts facts]
    (let [[acc' facts'] (reduce (fn [[acc' facts'] [k v]]
                                  (if (set/subset? v facts)
                                    [(conj acc' k) (conj facts' k)]
                                    [acc' facts']))
                                [acc facts]
                                deps)]
      (if (= facts facts')
        acc'
        (recur acc' facts')))))


(f #{:a :b :d :e} {:m #{:e :k :d}, :f #{:e :c :d}, :c #{:b :a}})

(f #{:a :b :d :e} {:m #{:e :k :d}, :c #{:b :a} :f #{:e :c :d}})

senya2215:08:01

Yes, that seems to be consistently returning #{:c :f} of 2 elements - you see any shortcomings with this approach?

saicheong15:08:35

@seancorfield: This page you recommended was helpful - I learnt that I can use :row-fn to read the clob returned from a query: http://clojure-doc.org/articles/ecosystem/java_jdbc/using_sql.html Here's how I did it:

(require '[clojure.java.jdbc :as j])
(defn clob-to-string [clob]
  (with-open [rdr ( (.getCharacterStream clob))]
    (apply str (line-seq rdr))))

(j/query db-spec ["select * from Report where refid = ?" "1"]
         {:row-fn #(clob-to-string (:body %))})

seancorfield16:08:20

@saicheong: Yes, that was the example I added to the gist that was posted earlier...

seancorfield16:08:14

Bah, it imported the Gist, not the comments...

seancorfield16:08:37

Anyways, hopefully that comment will show folks how to do it with the latest java.jdbc.

josh_tackett20:08:27

@seancorfield: I am using jdbc also. Not sure how to set up the db connection

josh_tackett20:08:01

(def mysql-db {:subprotocol "postgres" :subname :user :password })

josh_tackett20:08:01

Not sure what goes were as I have a db url and user and pass

josh_tackett20:08:02

does the url go in the subname?

josh_tackett20:08:24

Also getting this error when trying to insert:

java.lang.NullPointerException: null
         (Unknown Source) java.lang.Class.forName0
           Class.java:348 java.lang.Class.forName
             RT.java:2087 clojure.lang.RT.loadClassForName
             jdbc.clj:271 clojure.java.jdbc/get-connection
            jdbc.clj:1098 clojure.java.jdbc/insert-rows!
            jdbc.clj:1130 clojure.java.jdbc/insert!

josh_tackett20:08:32

(sql/insert! mysql-db :nlpanalytics 
  {:orgid "TEST"
   :userid "123"
   :incomingmessage "TEST INCOMING"
   :conversationid "456"}
  {:orgid "Rollio"
   :userid "SSAM"
   :incomingmessage "ROLLIO TEST"
   :conversationid "456”})

shooodooken20:08:22

Do many people use Spring Security for securing their API?

seancorfield20:08:04

@josh_tackett: The simplest form is to use {:dbtype "postgres" :dbname "mydb" :user "myuser" :password "mysecret"}

seancorfield20:08:18

That way you don’t have to worry about protocols and names etc.

seancorfield20:08:47

I need to send a PR to http://clojure-doc.org to get that updated (and should probably update the README in the repo).

seancorfield20:08:31

The error you’re getting is because the subprotocol would need to be "postgresql" for class-name (driver) auto-detection to work.

seancorfield20:08:45

That aliasing should work by default (`:subprotocol "postgres"` should work) so that’s a bug. I never use PostgreSQL so support for it is based mostly on folks in the community providing feedback.

seancorfield20:08:33

It is part of the core test suite locally, but that doesn’t extensively test db-spec formats, just SQL / DDL operations on known, good database specs.

josh_tackett20:08:38

@seancorfield: but where do I put the database url?

josh_tackett20:08:46

{:dbtype "postgres" :dbname "mydb" :user "myuser" :password "mysecret”} In this map there is no place to put my connection URL to actually write to the database

seancorfield23:08:15

@josh_tackett: java.jdbc creates the URL for you.

seancorfield23:08:25

If the host is not localhost, specify :host (which can be a hostname or just an IP address). If the port number is not PostgresSQL’s standard, specify :port.

seancorfield23:08:37

The idea is that you specify the bare minimum you need.

seancorfield23:08:01

Alternatively, you can specify the db spec as a string instead of a map — "" — or you can specify a map with just :connection-uri that is that URI string.