Thanks, I will try both your, and Bearophile's ideas and see if I
can figure out how they work.
reduce takes a range and eagerly (that is, not lazily) builds a value from it. In your case, it builds the squared distance between two points. The returned value can be anything: a numerical value, but also another range, a complex object, an so on. As long as it can be built increasingly, you're good.
Given a range [a,b,c,..., z] and a binary function f, reduce!(f)([a,b,c,...]) calculates f(a,b), then f(f(a,b), c), up to f(... f( f( f(a,b),c), d), ...,z).
The only question is whether you give it a seed value, or if it takes the first range element as seed (f(seed, a), f(f(seed,a),b) and son on). std.algorithm.reduce provides both overloads.
Note the only thing you get is the final result, not the internal f(f(...'s applications.
If you want a sum:
reduce!( (result,elem) => result + elem )(range)
or
reduce!( (result,elem) => result + elem )(range, 0)
Sum of squares:
reduce!( (result, elem) => result + elem^^2 )(range, 0)
In your case, the basic element is a tuple, coming from zip. Access to the two elements is done with [0] or [1]. So:
reduce!( (result, elem) => result + (elem[0]-elem[1])^^2 )(zippedRange, 0)
Of course, it would be easy to write a small n-range reduce helper function, that takes n ranges and reduces them in parallel:
reduce!( (result, a, b) => result + (a-b)^^2 )(rangeA, rangeB, 0)
Note that reduce is a versatile function: you can duplicate filter and map functionalities with it. The only thing to remember is that, compared to other iteration algorithm/ranges, it's eager. Don't use it on an infinite range!
We could also have a slightly different version that lazily produces the intermediate results as a range. IIRC, it's called 'scan' in Haskell. It's sometimes interesting to have it: you can interrupt the ongoing calculation when the result is 'good enough', not waiting for the entire input range to be consumed.