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.

Reply via email to