Yeah, I have to call you out on this one Dragan. I ran the following code: (ns fold-test (:require [uncomplicate.fluokitten.core :refer [foldmap]]))
(defn fa [& args] (println "fa " args) 1) (defn fb [& args] (println "fb " args) 1) (defn test-fold [] (foldmap fa nil fb [1 2 3] [4 5 6])) (test-fold) This code produced: fb (1 4) fa (nil 1) fb (2 5) fa (1 1) fb (3 6) fa (1 1) So I put a breakpoint in `fb` and ran it again. The stacktrace says it ends up in algo/collection-foldmap which we can see here: https://github.com/uncomplicate/fluokitten/blob/master/src/uncomplicate/fluokitten/algo.clj#L415-L443 That function is creating seqs out of all its arguments! So it really is not better than clojure.core/map as far as allocation is concerned. Timothy On Fri, Sep 23, 2016 at 5:15 PM, Francis Avila <fav...@breezeehr.com> wrote: > There are a few intermediate collections here: > > > 1. The source coll may produce a seq object. How costly this is > depends on the type of coll and the quality of its iterator/ireduce/seq > implementations. > 2. You may need to collect multiple source colls into a tuple-like > thing to produce a single object for the side-effecting function > 3. You may have an intermediate seq/coll of these tuple-like things. > 4. You may have a useless seq/coll of "output" from the side-effecting > function > > In the single-coll case: > > (map f col1) pays 1,4. > (doseq [x col1] (f x)) pays 1. > (run! f col1) pays 1 if coll has an inefficient IReduce, otherwise it pays > nothing. > (fold f col1) is the same (using reducers r/fold protocol for vectors, > which ultimately uses IReduce) > > In the multi-coll case: > > (map f coll1 col2) pays all four. > (run! (fn [[a b]] (f a b)) (map vector col1 col2)) pays 1, 2, and 3. > (doseq [[a b] (map vector col1 col2)] (f a b)) pays 1, 2, 3. > (fold f col1 col2) pays 1 from what I can see? (It uses first+next to walk > over the items stepwise? There's a lot of indirection so I'm not 100% sure > what the impl is for vectors that actually gets used.) > > There is no way to avoid 1 in the multi-step case (or 2 if you are fully > variadic), all you can do is use the most efficient-possible intermediate > object to track the traversal. Iterators are typically cheaper than seqs, > so the ideal case would be a loop-recur over multiple iterators. > > In the multi-coll case there is also no way IReduce can help. IReduce is a > trade: you give up the power to see each step of iteration in order to > allow the collection to perform the overall reduction operation more > efficiently. However with multi-coll you really do need to control the > iteration so you can get all the items at an index together. > > The ideal for multi-collection would probably be something that internally > looks like clojure.core/sequence but doesn't accumulate the results. > (Unfortunately some of the classes necessary to do this (MultiIterator) are > private.) > > Fluokitten could probably do it with some tweaking to its > algo/collection-foldmap to use iterators where possible instead of > first/next. > > > On Friday, September 23, 2016 at 5:23:51 PM UTC-5, Dragan Djuric wrote: >> >> fluokitten's fold is MUCH better than (map f a b) because it does NOT >> create intermediate collections. just use (fold f a b) and it would fold >> everything into one thing (in this case nil). If f is a function with side >> effects, it will invoke them. No intermediate collection is created AND the >> folding would be optimized per the type of a. >> >> On Friday, September 23, 2016 at 10:56:00 PM UTC+2, tbc++ wrote: >>> >>> How is fluokitten's fold any better than using seqs like (map f a b) >>> would? Both create intermediate collections. >>> >>> On Fri, Sep 23, 2016 at 11:40 AM, Dragan Djuric <drag...@gmail.com> >>> wrote: >>> >>>> If you do not insist on vanilla clojure, but can use a library, fold >>>> from fluokitten might enable you to do this. It is similar to reduce, but >>>> accepts multiple arguments. Give it a vararg folding function that prints >>>> what you need and ignores the first parameter, and you'd get what you asked >>>> for. >>>> >>>> >>>> On Friday, September 23, 2016 at 7:15:42 PM UTC+2, Mars0i wrote: >>>>> >>>>> On Friday, September 23, 2016 at 11:11:07 AM UTC-5, Alan Thompson >>>>> wrote: >>>>>> >>>>>> Huh. I was also unaware of the run! function. >>>>>> >>>>>> I suppose you could always write it like this: >>>>>> >>>>>> (def x (vec (range 3))) >>>>>> (def y (vec (reverse x))) >>>>>> >>>>>> (run! >>>>>> (fn [[x y]] (println x y)) >>>>>> >>>>>> (map vector x y)) >>>>>> >>>>>> >>>>>> > lein run >>>>>> 0 2 >>>>>> 1 1 >>>>>> 2 0 >>>>>> >>>>>> >>>>> Yes. But that's got the same problem. Doesn't matter with a toy >>>>> example, but the (map vector ...) could be undesirable with large >>>>> collections in performance-critical code. >>>>> >>>>> although the plain old for loop with dotimes looks simpler: >>>>>> >>>>>> (dotimes [i (count x) ] >>>>>> (println (x i) (y i))) >>>>>> >>>>>> >>>>>> maybe that is the best answer? It is hard to beat the flexibility of >>>>>> a a loop and an explicit index. >>>>>> >>>>> >>>>> I agree that this is clearer, but it kind of bothers me to index >>>>> through a vector sequentially in Clojure. We need indexing In Clojure >>>>> because sometimes you need to access a vector more arbitrarily. If you're >>>>> just walking the vector in order, we have better methods--as long as we >>>>> don't want to walk multiple vectors in the same order for side effects. >>>>> >>>>> However, the real drawback of the dotimes method is that it's not >>>>> efficient for the general case; it could be slow on lists, lazy sequences, >>>>> etc. (again, on non-toy examples). Many of the most convenient Clojure >>>>> functions return lazy sequences. Even the non-lazy sequences returned by >>>>> transducers aren't efficiently indexable, afaik. Of course you can always >>>>> throw any sequence into 'vec' and get out a vector, but that's an >>>>> unnecessary transformation if you just want to iterate through the >>>>> sequences element by element. >>>>> >>>>> If I'm writing a function that will plot points or that will write >>>>> data to a file, it shouldn't be a requirement for the sake of efficiency >>>>> that the data come in the form of vectors. I should be able to pass in >>>>> the >>>>> data in whatever form is easiest. Right now, if I wanted efficiency for >>>>> walking through sequences in the same order, without creating unnecessary >>>>> data structures, I'd have to write the function using loop/recur. On the >>>>> other hand, if I wanted the cross product of the sequences, I'd use doseq >>>>> and be done a lot quicker with clearer code. >>>>> >>>> -- >>>> You received this message because you are subscribed to the Google >>>> Groups "Clojure" group. >>>> To post to this group, send email to clo...@googlegroups.com >>>> Note that posts from new members are moderated - please be patient with >>>> your first post. >>>> To unsubscribe from this group, send email to >>>> clojure+u...@googlegroups.com >>>> For more options, visit this group at >>>> http://groups.google.com/group/clojure?hl=en >>>> --- >>>> You received this message because you are subscribed to the Google >>>> Groups "Clojure" group. >>>> To unsubscribe from this group and stop receiving emails from it, send >>>> an email to clojure+u...@googlegroups.com. >>>> For more options, visit https://groups.google.com/d/optout. >>>> >>> >>> >>> >>> -- >>> “One of the main causes of the fall of the Roman Empire was that–lacking >>> zero–they had no way to indicate successful termination of their C >>> programs.” >>> (Robert Firth) >>> >> -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@googlegroups.com > Note that posts from new members are moderated - please be patient with > your first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en > --- > You received this message because you are subscribed to the Google Groups > "Clojure" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to clojure+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. > -- “One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.” (Robert Firth) -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.