开发者

How to write a function or macro for "oneOf" ... casting in variadics?

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-items

You 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];
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜