3 ms·
Doesn't reduce force the accumulator to be shared though? Both the reduce and the lambda are holding onto references to acc, which defeats any "single reference
by edflsafoiewq 19d ago
Doesn't reduce force the accumulator to be shared though? Both the reduce and the lambda are holding onto references to acc, which defeats any "single reference" optimizations.
- ndriscoll 19d agodef reduce(acc, f): for v in self: acc = f(acc, v) return acc The current acc goes out of scope each time you call f. There's no shared reference (assuming f doesn't sneak store it elsewhere, which for string combining, f should just be `return a+b`?).
- edflsafoiewq 19d agoThe binding for acc in the reduce call is still active during the f call, which means there are at least two references to acc.
- ndriscoll 19d agoWhy is it still active? Even an interpreter with no lookahead could see that it goes out of scope immediately when f returns (it gets shadowed on that line), so as long as there's no guarantee about when finalizers get called, it should be able to mark it dead inside of reduce as soon as it's passed to f. Like move semantics here should be a general pattern for optimization, no?
- edflsafoiewq 19d agoDoes Python actually do that? If the f call throws, you can still observe the (unchanged) binding of acc in reduce.
- ndriscoll 19d agoFair, I suppose there's no end to the level of insanity that a programmer can do in a dynamic language. I'd think it could perhaps still look to see there's no catch, but maybe eval makes even that impossible.
- Zak 19d agoIt might - let's assume it does. My point is that it's better to use the explicit optimized method for joining strings in a performance-sensitive context than to try to meet the conditions for an implicit optimization.
- vhcr 19d agoThe problem with: ret = "" for s in strings: ret += s is that it re-allocates O(n) times, even if ret is referenced only once.
- edflsafoiewq 19d agoIf the s are small the usual geometric buffer growth mitigates that. Of course you can compute the final buffer size in this case, but often you have a bunch of dynamically-generated strings of different sizes.