c# - Should IEnumerable returned from OrderBy be evaluated if used multiple times? -
i'm looking @ code calls extension method orderby
. resulting ienumerable
potentially used multiple times. i've heard linq, better evaluate expressions if may used more once, because if don't linq query executed more once. case here also? (initially, looking @ code, didn't realise linq, see msdn documentation orderby
in linq namespace.)
to make concrete, code looks this, except item being enumerated more complicated int
, there may many more orders of magnitude more of them there in simple example.
ienumerable<int> multiply(ienumerable<int> list, int howmany, int k) { return list.take(howmany).select(i => * k); } void main() { int[] unsorted = { 1, 7, 3, 9, 4 }; ienumerable<int> sorted = unsorted.orderby(i=>i); // add .tolist() ? for(int k=1; k<=3; ++k) { ienumerable<int> multiplied = multiply(sorted, k, k); console.writeline(string.join(", ", multiplied)); } }
this code has same output regardless of whether use .tolist()
or not.
1 2, 6 3, 9, 12
it seems surprising code sorting on , on again. if is, , ought have .tolist()
, output same, how, in general, should know .tolist()
required? seeing magic words
deferred execution
in documentation?
to address @matt burland's suggestion should test performance myself changed program following (using double
s avoid overflow problems).
using system; using system.collections.generic; using system.linq; using system.diagnostics; namespace orderbyperformancetest { class program { static ienumerable<double> multiply(ienumerable<double> list, int howmany, double k) { return list.take(howmany).select(i => * k); } static void main(string[] args) { int n = 1000; ienumerable<double> unsorted = enumerable.range(0, n).select(i => (double)(n-i)); //console.writeline(string.join(", ", unsorted)); ienumerable<double> sorted1 = unsorted.orderby(i => i); // add .tolist() ? //console.writeline(string.join(", ", sorted1)); var sw = new stopwatch(); sw.start(); double sum = 0; (int k = 1; k <= n; ++k) { ienumerable<double> multiplied = multiply(sorted1, k, k); sum += multiplied.sum(); //console.writeline(string.join(", ", multiplied)); } sw.stop(); console.writeline("time {0}ms, sum {1}", sw.elapsedmilliseconds, sum); } } }
result:
- without
tolist
, 115ms - with
tolist
, 10ms
(sum
same in both cases)
when use linq expression, result of expression not calculated @ definition of expression when iterate on it.
if iterate multiple times result calculated (and can different if base list use in linq expression changed).
if use tolist() , keep result of method, result calculated , 1 time, , when iterate multiple times on result of tolist() method sure have same output.
Comments
Post a Comment