Fork me on GitHub
#meander
<
2022-04-04
>
Carlo11:04:46

I'm a bit confused by this interaction:

(-> {:a {:b 0.5 :c 0.5}
     :b {:c 1}
     :c {:a 1}}
    (meander/search
     (meander/scan [!sources {:c (meander/some _)}])
     !sources))
the result I get back is ([:a] [:b]) but I would have expected ([:a :b]) . I'm tring to find all map keys that contain :c in the submap. edit: I get why this happens: each search is a separate search, that only finds one source. But how could I find them all?

noprompt15:04:30

(m/find {:a {:b 0.5 :c 0.5}
         :b {:c 1}
         :c {:a 1}}
  (m/gather [!sources {:c (m/some _)}])
  !sources)

❤️ 1
Carlo18:04:03

Thanks for the solution, gather was the missing piece, wasn't aware it existed. I'll confess I still have a bit of difficulty in formulating the correct mental plan when I want to write something with meander. Case in point, the original problem I wanted to solve was turning:

{:a {:b 0.5 :c 0.5}
 :b {:c 1}
 :c {:a 1}}
into:
{:a {:c 1}
 :b {:a 0.5}
 :c {:a 0.5 :b 1}}
(which are two representation of a weighted graph: the first by outgoing edges, the second by incoming edges). It's still unclear to me how to jump from this solution to the complete one: I was trying something along the line of:
-> {:a {:b 0.5 :c 0.5}
     :b {:c 1}
     :c {:a 1}}
    (meander/find
     (meander/gather [!sources {:c (meander/some !weights)}])
     {:c  [!sources !weights]}))
but I don't see how to interleave !sources and weights to make them a map (almost all the combinations I tried return nil). Also, it doesn't seem possible to turn the key :c above in a logical variable. What should I think when solving this problem with meander?

Carlo18:04:08

(-> {:a {:b 0.5 :c 0.5}
     :b {:c 1}
     :c {:a 1}}
    (meander/find
     (meander/gather [!sources {:c (meander/some !weights)}])
     {:c (zipmap !sources !weights)}))
if only I could do this for each key. Maybe the solution is doing that step outside of meander?

noprompt19:04:30

Re: thinking: The problem should be broken down into steps rather than trying to be solved with one rule:

;; Step 1.
(m/search {:a {:b 0.5 :c 0.5}
           :b {:c 1}
           :c {:a 1}}
  {?k {?sk ?sv}}
  [[?sk ?k] ?sv])
;; =>
([[:b :a] 0.5]
 [[:c :a] 0.5]
 [[:c :b] 1]
 [[:a :c] 1])

;; Step 2.
(reduce
 (fn [m [path v]]
   (assoc-in m path v))
 {}
 '([[:b :a] 0.5]
   [[:c :a] 0.5]
   [[:c :b] 1]
   [[:a :c] 1]))
;; =>
{:a {:c 1}
 :b {:a 0.5}
 :c {:a 0.5 :b 1}}

🙌 1
Carlo19:04:40

ok, I'm glad that's the solution, as I did something similar in two steps (but didn't have the idea of grouping the keys to use them with assoc-in ; brilliant!) Thank you! 🙂

noprompt19:04:17

OK. Sorry, I made a small mistake in the previous post. Here is the corrected m/rewrite version:

(m/rewrite [{:a {:b 0.5 :c 0.5}
             :b {:c 1}
             :c {:a 1}}
            {}]
  [{?k {?sk ?sv & ?m-k} & ?m-in}
   {?sk ?m-ko & ?m-out}]
  (m/cata [{?k ?m-k & ?m-in}
           {?sk {?k ?sv & ?m-ko} & ?m-out}])

  [{?k {?sk ?sv & ?m-k} & ?m-in}
   ?m-out]
  (m/cata [{?k ?m-k & ?m-in}
           {?sk {?k ?sv} & ?m-out}])

  [_ ?m-out]
  ?m-out)
;; =>
{:b {:a 0.5}, :c {:b 1, :a 0.5}, :a {:c 1}}

noprompt19:04:02

Using a step-wise approach:

(let [step (fn [pair]
             (m/rewrite pair
               [{?k {?sk ?sv & ?m-k} & ?m-in}
                {?sk ?m-ko & ?m-out}]
               [{?k ?m-k & ?m-in}
                {?sk {?k ?sv & ?m-ko} & ?m-out}]

               [{?k {?sk ?sv & ?m-k} & ?m-in}
                ?m-out]
               [{?k ?m-k & ?m-in}
                {?sk {?k ?sv} & ?m-out}]

               [_ ?m-out]
               ?m-out))
      init [{:a {:b 0.5 :c 0.5}
             :b {:c 1}
             :c {:a 1}}
            {}]]
  (-> init
      step ;; => [{:b {:c 1}, :c {:a 1}, :a {:c 0.5}} {:b {:a 0.5}}]
      step ;; => [{:c {:a 1}, :a {:c 0.5}, :b {}} {:b {:a 0.5}, :c {:b 1}}]
      step ;; => [{:c {:a 1}, :b {}, :a {}} {:b {:a 0.5}, :c {:b 1, :a 0.5}}]
      step ;; => [{:b {}, :a {}, :c {}} {:b {:a 0.5}, :c {:b 1, :a 0.5}, :a {:c 1}}]
      step))
;; =>
{:b {:c 0.5, :a 0.5},
 :c {:a 0.5},
 :a {:c 1}}

noprompt19:04:09

The second case can be handled in the first case with m/or and m/let:

(m/rewrite [{:a {:b 0.5 :c 0.5}
             :b {:c 1}
             :c {:a 1}}
            {}]
  [{?k {?sk ?sv & ?m-k} & ?m-in}
   (m/or {?sk ?m-ko & ?m-out}
         (m/let [?m-ko {}] ?m-out))]
  (m/cata [{?k ?m-k & ?m-in}
           {?sk {?k ?sv & ?m-ko} & ?m-out}])

  [_ ?m-out]
  ?m-out)
;; =>
{:b {:a 0.5}, :a {:c 1}, :c {:b 1, :a 0.5}}

noprompt19:04:45

This is to demonstrate that you can do this kind of thing but not necessarily that you should do this kind of thing. 🙂

noprompt19:04:20

Find a balance that works for you!

noprompt15:04:25

@meditans search is for finding all possible solutions, find is for finding one (or none). The reason the search returns is ([:a] [:b]) is because it there are two solutions and !sources is a singleton vector where !sources matched once for each of the solutions. m/scan is sugar for

(m/seqable & _ <patterns ...> & _)
The & _ on either side of <patterns ...> is the flexible, ambiguous space and <patterns ...> is what is being matched.

🙌 1
noprompt16:04:00

(m/search [1 2 3 4 5 6]
  (m/scan (m/pred number? !number))
  !number)
;; =>
([1] [2] [3] [4] [5] [6])