Functions can only be compared by identity, not value, eg (not=
(constantly 1) (constantly 1)). When you attach metadata to a
function, you cause it to no longer be = to other non-meta instances
of that function, so memoize can no longer use previous values.
user=> (def mp (memoize println))
#'user/mp
user=> (def x (constantly 1))
#'user/x
user=> (mp x)
#<core$constantly$fn__3551 clojure.core$constantly$fn__3551@11e31ea>
nil
user=> (mp x)
nil
user=> (mp (with-meta x {:x 1}))
#<core$constantly$fn__3551 clojure.core$constantly$fn__3551@507369>
nil
user=> (mp (with-meta x {:x 1}))
#<core$constantly$fn__3551 clojure.core$constantly$fn__3551@1d71e34>
nil
user=> (mp (with-meta x {:x 1}))
#<core$constantly$fn__3551 clojure.core$constantly$fn__3551@1fac733>
nil
Note that the printed representation of the function is changing, too:
it has a different @address because it is a new object with different
metadata.
On Mar 10, 3:33 pm, Tassilo Horn <[email protected]> wrote:
> Hi all,
>
> I'm struggeling with some really strange problem. I have a function
> `p-apply', which can be used find all reachable vertices in a graph
> matching a regular expression modeled as nested vector of functions, to
> which the results are chained through. Here's an example call:
>
> (p-apply v1 [p-seq --> --> [p-alt --<>
> [p-+ [--> :cls 'Foo]]]])
>
> That means, starting from v1 you first have to traverse 2 outgoing
> edges, and then you may either traverse an aggregation from part to
> whole side, or you may iterate one or many outgoing edges of type Foo.
>
> Sometimes, memoization can bring a performance win, so I use a global
> var with a macro to dispatch to internal p-apply functions.
>
> Here's the relevant parts of the code. You can basically jump to the
> last function.
>
> --8<---------------cut here---------------start------------->8---
> (def ^{:private true}
> *memoize-path-eval* false)
>
> (defn- p-apply-normal
> [v p]
> (cond
> ;; funs -->
> (fn? p) (p v)
> ;; funs with params [--> :cls 'Foo]
> (coll? p) (apply (first p) v (next p))
> ;; adjacences / role names
> (or (string? p) (keyword? p) (symbol? p))
> (let [vs (into-oset v)]
> (apply into-oset (map #(adjacences % p) vs)))
> :else (throw (RuntimeException.
> (format "Don't know how to apply %s."
> p)))))
>
> (def ^{:private true}
> p-apply-memoized
> (memoize p-apply-normal))
>
> (defmacro with-path-eval-memoization
> [& body]
> `(binding [*memoize-path-eval* true]
> ~@body))
>
> (defn- p-apply-internal
> [v p]
> (if *memoize-path-eval*
> (let [vs (into-oset v)
> r (map #(p-apply-memoized % p) vs)]
> (if (seq r)
> (apply into-oset r)
> (ordered-set)))
> (p-apply-normal v p)))
>
> (defn p-apply
> [v p]
> (let [counter (atom -1)]
> (p-apply-internal
> v
>
> ;;; Using p with metadata somehow makes things SLOW
> ;(clojure.walk/postwalk #(if (fn? %)
> ; (with-meta % {:state (swap! counter inc)})
> ; %) p)
>
> ;;; using just p is FAST
> p
> )))
> --8<---------------cut here---------------end--------------->8---
>
> Because I'd like to extend the search to collect also the shortest paths
> to all reachable vertices that conform the given regular expression, I
> wanted to put metadata on the function symbols that are given to
> `p-apply'.
>
> But as soon as I comment `p' and uncomment the `postwalk' call returning
> `p' with metadata on the function symbols, memoization seems to stop
> working.
>
> With the code above (`p' passed to `p-apply-internal'), I get these
> timings with memoization turned on:
>
> With memoization
> ================
> cbo-q-sequential
> "Elapsed time: 6805.37119 msecs"
> cbo-q-parallel
> "Elapsed time: 212.276041 msecs"
>
> With memoization 2
> ==================
> cbo-q-sequential
> "Elapsed time: 178.102362 msecs"
> cbo-q-parallel
> "Elapsed time: 172.027562 msecs"
>
> So the first function, which in effect fills the memoization cache and
> doesn't much benefit from it itself runs ~7 secs, but repeating the
> queries is lightning fast.
>
> If I comment `p' and pass it first through `postwalk' to put metadata on
> the functions, then the timings are these:
>
> With memoization
> ================
> cbo-q-sequential
> "Elapsed time: 8247.753366 msecs"
> cbo-q-parallel
> "Elapsed time: 8962.930698 msecs"
>
> With memoization 2
> ==================
> cbo-q-sequential
> "Elapsed time: 11010.302911 msecs"
> cbo-q-parallel
> "Elapsed time: 9098.196259 msecs"
>
> What's the matter? Why does it get that slow only because of metadata?
>
> Bye,
> Tassilo
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en