How to avoid duplication in a for loop when “initialization step” is identical to “update step”?
https://softwareengineering.stackexchange.com/questions/398968
-
02-03-2021 - |
質問
I often find a situation where I need to write duplicate codes in a for loop, where the "init step" to identical to the "update step":
// duplicate `next()`
for (let x = next(); p(x); x = next()) {
f(x)
}
// the same in a while loop
let x = next()
while (p(x)) {
f(x)
x = next()
}
It is not much a problem when next()
is short, but often it's not:
// problem is evident when next is long or even inline
let x = mmmmm(rewqreqw(rewqrqew()), rewqrewq(), rewqreqw())
while (p(x)) {
f(x)
x = mmmmm(rewqreqw(rewqrqew()), rewqrewq(), rewqreqw())
}
How can I deal with this situation?
解決
There are multiple common approaches to avoid repeating a complex update-step:
Use a lambda:
auto g = [&]{ return mmmmm(rewqreqw(rewqrqew()), rewqrewq(), rewqreqw()); }; for (auto x = g(); p(x); x = g(x)) f(x);
Use a dummy initialization and assign in the condition:
auto x = dummy; while (p(x = mmmmm(rewqreqw(rewqrqew()), rewqrewq(), rewqreqw()))) f(x);
A variant using the comma-operator:
auto x = dummy; while (x = mmmmm(rewqreqw(rewqrqew()), rewqrewq(), rewqreqw()), p(x)) f(x);
Use an infinite loop with a
break
:for (;;) { auto x = mmmmm(rewqreqw(rewqrqew()), rewqrewq(), rewqreqw()); if (!p(x)) break; f(x); }
Which looks and performs best in your case?
You decide.
他のヒント
I'm not sure what language you're using, but many languages have an abstraction already for calling a function to produce the next element of a sequence, in a more precise way than a for or while loop. It's often called an iterator or a generator.
For example, in Scala, you can write:
Iterator.continually(next()).takeWhile(p).map(f)
If your language supports higher-order functions, you can create your own abstraction easily. It's also sometimes implemented in OOP languages by implementing an interface with something like a getNext
and a hasNext
function.