Skip to content

KVC: Set And Array Operators @avg

2010.07.31

KVC (Key-Value Coding) の Set And Array Operators をマスターすると、コードを簡素化できたり、NSPredicate でできることの幅が増えたり、Cocoa Bindings でできることの幅が増えるなどの利点があります。初回である前回は、Set And Array Operators の概要を説明しました。

今回は、@avg オペレータを取り上げます。


@avg オペレータは、平均値を演算するためのオペレータです。valueForKeyPath: で得られる値を double に変換し、その平均値を NSNumber で返します。

例えば、次のような NSDictionary 型変数 company があるとします。

company = {
    employees = (
        {
            age = 20;
            name = A;
        },
        {
            age = 23;
            name = B;
        },
        {
            age = 20;
            name = C;
        }
    );
}

このとき、age の平均値を以下のように求めることができます。

NSNumber *averageOfAge;
averageOfAge = [company valueForKeyPath:@"employees.@avg.age"];

上の例で averageOfAge は (20 + 23 + 20) / 3 で 21 となります。

@avg オペレータを使わずに age の平均値を求める場合、コードは以下のようになります。

double averageOfAge = 0.0;
NSArray *ages;
ages = [company valueForKeyPath:@"employees.age"];
for (NSNumber *age in ages) {
    averageOfAge += [age doubleValue] / (double)[ages count];
}

@avg オペレータを使用した方が、簡潔に結果を求めることができます。

パフォーマンスはどうでしょうか。比較してみました。
比較には以下のコードを使用しました。使用した機種はMacBook Pro。Mac アプリとして作成し、実行しました。

@implementation avgAppDelegate

- (NSDictionary*)company
{
    // Make employees
    NSMutableArray *employees;
    NSDictionary *employee;
    employees = [NSMutableArray array];
    for (NSUInteger i = 0; i < 10000; i++) {
        employee = [NSDictionary
            dictionaryWithObject:[NSNumber numberWithUnsignedInt:arc4random()]
            forKey:@"age"];
        [employees addObject:employee];
    }
    
    // Make company
    NSDictionary *company;
    company = [NSDictionary
        dictionaryWithObject:employees
        forKey:@"employees"];
    
    return company;
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    CFAbsoluteTime startTime;
    CFTimeInterval sumOfTime;
    NSUInteger const count = 100;
    
    // Get company
    NSDictionary *company;
    company = [self company];
    
    // @avg version
    sumOfTime = 0.0;
    for (int i = 0; i < count; i++) {
        // Make pool
        NSAutoreleasePool *pool;
        pool = [[NSAutoreleasePool alloc] init];
        
        // Record startTime
        startTime = CFAbsoluteTimeGetCurrent();
        
        // Get averageOfAge using @avg
        NSNumber *averageOfAge;
        averageOfAge = [company valueForKeyPath:
            @"employees.@avg.age"];
        
        // Update sumOfTime
        sumOfTime += (CFAbsoluteTimeGetCurrent() - startTime);
        
        // Release pool
        [pool release];
    }
    NSLog(@"time average = %f", sumOfTime / (double)count);
    
    // Manual version
    sumOfTime = 0.0;
    for (int i = 0; i < count; i++) {
        // Make pool
        NSAutoreleasePool *pool;
        pool = [[NSAutoreleasePool alloc] init];
        
        // Record startTime
        startTime = CFAbsoluteTimeGetCurrent();
        
        // Get averageOfAge manually
        double averageOfAge = 0.0;
        NSArray *ages;
        ages = [company valueForKeyPath:@"employees.age"];
        for (NSNumber *age in ages) {
            averageOfAge += [age doubleValue] / (double)[ages count];
        }
        
        // Update sumOfTime
        sumOfTime += (CFAbsoluteTimeGetCurrent() - startTime);
        
        // Release pool
        [pool release];
    }
    NSLog(@"time average = %f", sumOfTime / (double)count);
}

@end

測定結果は以下の通りになりました (3回の平均):

@avg version 0.048614 s
Manual version 0.001575 s

@avg オペレータを使用すると速度は遅くなる、という結果が出ました。@avg オペレータを使用すべきかどうかは、ケース・バイ・ケースということですね。

次回は、@sum オペレータを取り上げます。

Advertisements

From → Develop

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s