Fork me on GitHub
#specter
<
2017-12-17
>
jrheard02:12:04

thanks nathan! i’ll take a closer look at this tomorrow, much appreciated! 🙂

jrheard22:12:36

that absolutely did what i wanted - i added bounds checking and added my desired reversing behavior when row2 < row or col2 < col, and ended up with this, which i haven’t tested thoroughly but seems to be correct so far:

jrheard22:12:40

(defn ^:direct-nav grid-values
  [x1 y1 x2 y2]
  (if (g/cell-is-on-grid x1 y1)
    (let [x2 (bound-between x2 0 (dec GRID-WIDTH))
          y2 (bound-between y2 0 (dec GRID-HEIGHT))]

      (reduce
        multi-path
        (for [x (if (< x1 x2)
                  (range x1 (inc x2))
                  (reverse (range x2 (inc x1))))
              y (if (< y1 y2)
                  (range y1 (inc y2))
                  (reverse (range y2 (inc y1))))]
          (nthpath x y))))

    STOP))

jrheard22:12:49

thanks so much for your help, i really appreciate it!

jrheard22:12:21

final usage ends up looking like this, replacing my reduce from earlier:

(fn [xdir ydir]
   (let [values-in-direction (select (grid-values (+ x xdir)
                                                  (+ y ydir)
                                                  (+ x (* xdir MAX-RUN-LENGTH))
                                                  (+ y (* ydir MAX-RUN-LENGTH)))
                                     grid)
         run-values (take-while (comp not nil?) values-in-direction)]
     [(count run-values) (apply + run-values)]))

jrheard22:12:49

seems to be way slower than my previous reduce implementation though, dang - likely some mistake on my end, will profile

jrheard22:12:25

ok yeah now my program spends 88% of its time in com.rpl.specter.impl.mk_dynamic_path_maker

jrheard22:12:12

the specterized code above is awesome though, i really like it way more than my handrolled reduce from yesterday - it untangles the computation into its component parts (first get a list of up to 5 values in this direction, then drop everything after and including the first nil, then count the values and sum them)

jrheard22:12:20

whereas my previous code did all those things at once

jrheard22:12:41

so i remain a big fan of specter and also remain appreciative of your help, but am not sure how to proceed performancewise

jrheard22:12:03

it’s the weekend, this is a toy project and non-urgent, no rush on a reply 🙂 thanks!

jrheard23:12:17

if it’s helpful, a screenshot of the profile data: https://www.evernote.com/shard/s11/sh/73bf32be-7f86-414d-adcb-d61279942fd2/b3618565f1ea939f (percentages shown are different because i’ve filtered for com.rpl.specter, i believe)

nathanmarz23:12:25

@jrheard it would be a lot faster as a first-class navigator

nathanmarz23:12:45

implementation would be similar to ALL, except in two dimensions

nathanmarz23:12:59

it would be easy to also avoid needing to do two nth per element, since you can do every matching value in a row one after another

jrheard23:12:57

interesting, thanks for the tip! i’ll read through the docs and investigate that line of attack

jrheard23:12:29

hm, can’t tell if this is a foolish question - would the implementation actually look more like srange rather than all? the main difference i see is that srange takes arguments start and end, and my thing will also take arguments (`x1`, y1, x2, y2)

jrheard23:12:11

also, i notice that ALL uses doseqres ( https://github.com/nathanmarz/specter/blob/5efafd2d9bc2714fd87ff81b1268ae6f88256a81/src/clj/com/rpl/specter/util_macros.clj#L3 ) - should i figure out what doseqres does, or can i safely ignore it?

nathanmarz23:12:55

@jrheard I mean it's going to be similar in function, navigating to many subvalues of a data structure

nathanmarz23:12:06

implementation-wise it will be very different

jrheard23:12:18

i ended up with this, it’s 4-5x faster than the previous specter approach but still roughly (no benchmarks collected, going solely on how the app feels to use) 1.5-2x slower than the reduce approach. it’s also kind of hideous but that’s my fault:

(defnav
  grid-values-2
  [x1 y1 x2 y2]
  (select* [this structure next-fn]
           (assert (or (= x1 x2)
                       (= y1 y2)))

           (next-fn
             (if (cell-is-on-grid x1 y1)
               (let [x2 (bound-between x2 0 (dec GRID-WIDTH))
                     y2 (bound-between y2 0 (dec GRID-HEIGHT))]
                 (if (= x1 x2)
                   (let [column (nth structure x1)]
                     (if (< y1 y2)
                       (subvec column y1 (inc y2))
                       (reverse (subvec column y2 (inc y1)))))

                   (for [x (if (< x1 x2)
                             (range x1 (inc x2))
                             (reverse (range x2 (inc x1))))]
                     (-> structure
                         (nth x)
                         (nth y1)))))
               [])))

  (transform* [this structure next-fn]
              ; punting on this for now
              (assert false)))