Best way to add a "forCount" control structure to Objective-C?
Adam Ko has provided a magnificent solution to this question, thanks Adam Ko.
BTW if, like me, you love the c preprocessor (the thing that handles #defines), you may not be aware there is a handy thing in XCode: right click on the body of one of your open source files, go down near the bottom .. "Preprocess". It actually runs the preprocessor, showing you the overall "real deal" of what is going to be compiled. It's great!
This question is a matter of style and code clarity. Consider it similar to questions about subtle naming issues, or the best choice (more readable, more maintainable) among available idioms.
As a matter of course, one uses loops like this:
for(NSUInteger _i=0; _i<20; ++_i)
{
.. do this 20 times ..
}
To be clear, the effect is to to do something N times. (You are not using the index in the body.)
I want to signal clearly for the reader that this is a count-based loop -- ie, the index is irrelevant and algorithmically we are doing something N times.
Hence I want a clean way to do a body N times, with no imperial entanglements or romantic commitments. You could make a macro like this:
#define forCount(N) for(NSUinteger __neverused=0; __neverused<N; ++__neverused)
and that works. Hence,
forCount(20)
{
.. do this 20 times ..
}
However, conceivably the "hidden" variable used there could cause trouble if it collided with something in the future. (Perhaps if you nested the control structure in question, among other problems.)
To be clear efficiency, etc., is not the issue here. There are already a few different control structures (while, do, etc etc) that are actually of course exactly the same thing, but which exist only as a matter of style and to indicate clearly to the reader the intended algorithmic meaning of the code passage in question. "forCount" is another such needed control structure, because "index-irrelevant" count loops are completely basic in any algorithmic programming.
Does anyone know the really, really, REALLY cool solution to this? The #define mentioned is just not satisfying, and you've thrown in a variable name that inevitably someone will step on.
Thanks!
Later...
A couple of people have asked essentially "But why do it?"
开发者_StackOverflowLook at the following two code examples:
for ( spaceship = 3; spaceship < 8; ++spaceship )
{
beginWarpEffectForShip( spaceship )
}
forCount( 25 )
{
addARandomComet
}
Of course the effect is utterly and dramatically different for the reader.
After all, there are alresdy numerous (totally identical) control structures in c, where the only difference is style: that is to say, conveying content to the reader.
We all use "non-index-relative" loops ("do something 5 times") every time we touch a keyboard, it's as natural as pie.
So, the #define is an OKish solution, is there a better way to do it? Cheers
You could use blocks for that. For instance,
void forCount(NSUInteger count, void(^block)()) {
for (NSUInteger i = 0; i < count; i++) block();
}
and it could be used like:
forCount(5, ^{
// Do something in the outer loop
forCount(10, ^{
// Do something in the inner loop
});
});
Be warned that if you need to write to variables declared outside the blocks you need to specify the __block
storage qualifier.
A better way is to do this to allow nested forCount structure -
#define $_TOKENPASTE(x,y) x##y
#define $$TOKENPASTE(x,y) $_TOKENPASTE(x, y)
#define $itr $$TOKENPASTE($_itr_,__LINE__)
#define forCount(N) for (NSUInteger $itr=0; $itr<N; ++$itr)
Then you can use it like this
forCount(5)
{
forCount(10)
{
printf("Hello, World!\n");
}
}
Edit: The problem you suggested in your comment can be fixed easily. Simply change the above macro to become
#define $_TOKENPASTE(x,y) x##y
#define $$TOKENPASTE(x,y) $_TOKENPASTE(x, y)
#define UVAR(var) $$TOKENPASTE(var,__LINE__)
#define forCount(N) for (NSUInteger UVAR($itr)=0, UVAR($max)=(NSUInteger)(N); \
UVAR($itr)<UVAR($max); ++UVAR($itr))
What it does is that it reads the value of the expression you give in the parameter of forCount
, and use the value to iterate, that way you avoid multiple evaluations.
On possibility would be to use dispatch_apply()
:
dispatch_apply(25, myQueue, ^(size_t iterationNumber) {
... do stuff ...
});
Note that this supports both concurrent and synchronous execution, depending on whether myQueue
is one of the concurrent queues or a serial queue of your own creation.
To be honest, I think you're over addressing a non-issue.
If want to iterate over an entire collection use the Objective-C 2 style iterators, if you only want to iterate a finite number of times just use a standard for loop - the memory space you loose from an otherwise un-used integer is meaningless.
Wrapping such standard approaches up just feels un-necessary and counter-intuitive.
No, there is no cooler solution (not with Apple's GCC version anyways). The level at which C works requires you to explicitly have counters for every task that require counting, and the language defines no way to create new control structures.
Other compilers/versions of GCC have a __COUNTER__
macro that I suppose could somehow be used with preprocessor pasting to create unique identifiers, but I couldn't figure a way to use it to declare identifiers in a useful way.
What's so unclean about declaring a variable in the for
and never using it in its body anyways?
FYI You could combine the below code with a define, or write something for the reader to the effect of:
//Assign an integer variable to 0.
int j = 0;
do{
//do something as many times as specified in the while part
}while(++j < 20);
Why not take the name of the variable in the macro? Something like this:
#define forCount(N, name) for(NSUInteger name; name < N; name++)
Then if you wanted to nest your control structures:
forCount(20, i) {
// Do some work.
forCount(100, j) {
// Do more work.
}
}
精彩评论