There are a few typos and defn is missing from fb in my last message, but I 
hope it is still readable. Sorry, I am typing this on a mobile device while 
watching the last episode of The Man in the High Castle :) Also, I am 
talking about the code I wrote years ago from the top of my mind without 
access to the repl :)

On Saturday, September 24, 2016 at 1:44:10 AM UTC+2, Dragan Djuric wrote:
>
> A couple of things:
> 1. How fold/foldmap and any other function works, depends on the actual 
> type. For example, if you look at 
> https://github.com/uncomplicate/neanderthal/blob/master/src/clojure/uncomplicate/neanderthal/impl/fluokitten.clj#L396
>  
> you can see that there no intermediate allocations, and everything is 
> primitive.
> 2. Now, if you give foldmap a sequence (or a vector), it goes to the 
> implementation that you pointed to. Now, the difference from map: if I 
> understand well, map would produce an unnecessary resulting sequence. 
> foldmap does not. your accumulating function does need to return 1, or 
> sequences - why not return nil? Also, the accumulator is nil, and can use 
> any dummy function that just nils everything. There is only a matter of 
> calling first/next. Do they really produce any new instance objects? That 
> depends on the implementation of seq, I believe, but it's the same even if 
> we used loop/recur, I believe? 
> 3. The sequences in your printout results are the result of how clojure 
> treat varargs, or I am missing something. So, if I give it a function such 
> as (fb [_ a b] (println a b)), what is exactly allocated, that is not 
> allocated even when using loop/recur directly with first/next?
>
> On Saturday, September 24, 2016 at 1:25:14 AM UTC+2, tbc++ wrote:
>>
>> 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 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