Note - using variadic arguments, Phix has provided a super solution below. However it gives compiler warnings when used with integers since you're casting them to an id, and it will not work with for example floats.
Question -- how write a facility that chooses one item from a list? Ideally, it would be as flexible as possib开发者_如何学Gole regarding the type of the items/objects/whatever in the list.
Example usage:
NSInteger openingMap = [utilities oneOf:1, 2, 3, 7, 8];
initialAngle =oneOf(1.25, 1.75, 1.95, 2.00, 2.01);
self.spaceShipNickName =oneOf(@"Blaster",@"Blitzen",@"Stardancer",@"Quantum");
self.chassisProperty = oneOf(titanium, neutronium, unavailablium);
[fireworksLayer paintStars:oneOf(blue,green,white) howMany:oneOf(20,25,50)];
[trump chooseDefaultSuite:oneOf(diamonds,hearts,clubs,spades)];
// normally have a few explosions, but, occasionally have a huge display...
explosionCount = oneOf( 2,2,2,3,4,1,28,3,3,3,70 );
Note that some examples are integers, some enums, some NSStrings, etc. So again, it would be most impressive if the one facility could handle different types. (Rather than perhaps a related group, like ... oneOfIntegers, oneOfStrings, oneOfObject, whatever.)
It goes without saying that to choose the random item, just use...
= arcrandom() % number-of-itemsYou could use an Objective C method or class, a c function, or some sort of system of macros, category extension to NSMutableArray, or indeed blocks - or anything else. It should be as flexible as possible for broad use anywhere in a typical iOS or Mac project... any ideas?
#define NUMARGS(...) (sizeof((id[]){__VA_ARGS__})/sizeof(id))
#define ONEOF(...) (oneOf(NUMARGS(__VA_ARGS__), __VA_ARGS__))
id oneOf(int numargs, ...) {
va_list ap;
va_start(ap,numargs);
int i = arc4random() % numargs;
id val = nil;
do {
val = va_arg(ap, id);
} while (i--);
va_end(ap);
return val;
}
Usage:
NSLog(@"%@", ONEOF(@"Blaster",@"Blitzen",@"Stardancer",@"Quantum"));
NSLog(@"%d", ONEOF( 2,2,2,3,4,1,28,3,3,3,70 ));
Note, that both work, however the latter throws some compiler warnings.
I'd put it as a category on NSArray (warning - this code is untested and probably has off-by-one errors in!)
@interface NSArray (one_of)
- (id)anyObject;
@end
@implementation NSArray (one_of)
- (id)anyObject {
if (0 == [self count]) return nil;
if (1 == [self count]) return [self objectAtIndex:0];
return [self objectAtIndex:(arcrandom() % [self count])];
}
@end
Then, use it like :
NSString *thingy = [[NSArray arrayWithObjects:@"1", @"2", @"3"] anyObject];
NB To handle numbers (and other native types etc) you must make them objects i.e.
NSInteger number = [[[NSArray arrayWithObjects:
[NSNumber numberWithInteger:1],
[NSNumber numberWithInteger:2],
[NSNumber numberWithInteger:3], nil] anyObject] intValue];
精彩评论