独自の型を持つ要素の配列をソートするには、NSArrayクラスの「sortedArrayUsingSelector」メソッドを使います。
例えば、TestClassという独自のクラスを配列にしてソートさせたい場合、以下のようにします。
@interface TestClass : NSObject {
@public
NSString *scale;
}
-(NSComparisonResult)compare_func:(TestClass*)a; // ソート時に呼ばれます
@property (nonatomic, retain) NSString *scale;
@end
@implementation TestClass
@synthesize scale;
-(id)init
{
self = [super init];
if (self) {
scale = @"ちゅうくらい";
}
return self;
}
-(id)initWithString:(NSString*)str
{
self = [super init];
if (self) {
scale = [str retain];
}
return self;
}
-(NSComparisonResult)compare_func:(TestClass*)a // ソート時に呼ばれます
{
if ([self.scale isEqualToString:@"ちゅうくらい"]) {
if ([a.scale isEqualToString:@"おおきい"]) {
return NSOrderedAscending;
} else if ([a.scale isEqualToString:@"ちゅうくらい"]) {
return NSOrderedSame;
} else {
return NSOrderedDescending;
}
} else if ([self.scale isEqualToString:@"おおきい"]) {
return NSOrderedDescending;
} else if ([self.scale isEqualToString:@"ちいさい"]) {
return NSOrderedAscending;
}
return NSOrderedAscending;
}
@end
ソート処理の中身とかは別にどうでも良いのですが、重要なのはソート処理に使うメソッドを用意しておくということです。
このクラスを配列にしてソートさせるには、以下のようにします。
// 独自クラス(TestClass)の配列を生成
NSArray *test_array = [[NSArray arrayWithObjects:
[[[TestClass alloc] init] autorelease]
, [[[TestClass alloc] initWithString:@"おおきい"] autorelease]
, [[[TestClass alloc] initWithString:@"ちいさい"] autorelease]
, nil] retain];
// ソート
NSArray *sorted_array = [test_array sortedArrayUsingSelector:@selector(compare_func:)];
// 結果を出力
for (TestClass *tc in sorted_array) {
NSLog(@"%@", tc.scale);
}
NSArrayクラスの「sortedArrayUsingSelector」メソッドの引数に独自クラスに実装したソート用のメソッド(compare_func:)を指定することで、NSArrayがソート時に呼び出してくれるようになります。
結果は以下のようになります。
ちいさい
ちゅうくらい
おおきい
NSArrayクラスの「sortedArrayUsingSelector」メソッドは要素となるクラスにソート処理を実装しなければなりませんが、「sortedArrayUsingFunction」メソッドはソート処理を外部の関数に指定することができます。
// ソート処理を配列要素ではなく、外部の関数に定義
NSInteger compare_func(TestClass *a, TestClass *b, void* context)
{
if ([a.scale isEqualToString:@"ちゅうくらい"]) {
if ([b.scale isEqualToString:@"おおきい"]) {
return NSOrderedAscending;
} else if ([b.scale isEqualToString:@"ちゅうくらい"]) {
return NSOrderedSame;
} else {
return NSOrderedDescending;
}
} else if ([a.scale isEqualToString:@"おおきい"]) {
return NSOrderedDescending;
} else if ([a.scale isEqualToString:@"ちいさい"]) {
return NSOrderedAscending;
}
return NSOrderedAscending;
}
int main (int argc, const char * argv[])
{
NSArray *test_array = [[NSArray arrayWithObjects:[[[TestClass alloc] init] autorelease]
, [[[TestClass alloc] initWithString:@"おおきい"] autorelease]
, [[[TestClass alloc] initWithString:@"ちいさい"] autorelease]
, nil] retain];
NSArray *sorted_array = [test_array sortedArrayUsingFunction:compare_func context:NULL];
for (TestClass *tc in sorted_array) {
NSLog(@"%@", tc.scale);
}
return 0;
}
結果は先の結果と同じになります。
2013年3月15日金曜日
2013年3月14日木曜日
NSArrayのソート
NSArrayをソートするには、sortedArrayUsingComparatorメソッドを使います。
数値を降順にソートする場合
NSArray *num_array = [NSArray arrayWithObjects:[NSNumber numberWithInt:0]
, [NSNumber numberWithInt:3]
, [NSNumber numberWithInt:2]
, [NSNumber numberWithInt:4]
, [NSNumber numberWithInt:1]
, [NSNumber numberWithInt:7]
, [NSNumber numberWithInt:10]
, [NSNumber numberWithInt:9]
, [NSNumber numberWithInt:8]
, [NSNumber numberWithInt:5]
, nil];
NSArray *sorted_array = [num_array sortedArrayUsingComparator:^NSComparisonResult(NSNumber *a, NSNumber *b) {
return b.intValue - a.intValue; // ソート
}];
for (NSNumber *aNum in sorted_array) {
NSLog(@"sorted_num:%@", aNum);
}
上記のブロック関数内のaとbを入れ替えてやれば昇順にソートさせることもできます。
結果は以下のようになります。
sorted_num:10
sorted_num:9
sorted_num:8
sorted_num:7
sorted_num:5
sorted_num:4
sorted_num:3
sorted_num:2
sorted_num:1
sorted_num:0
文字を降順にソートする場合
NSArray *str_array = [NSArray arrayWithObjects:@"A", @"E", @"B", @"Z", @"C", @"F", @"D", nil];
sorted_array = [str_array sortedArrayUsingComparator:^NSComparisonResult(NSString *a, NSString *b) {
return [b compare:a]; // ソート
}];
for (NSString *str in sorted_array) {
NSLog(@"sorted_str:%@", str);
}
上記のブロック関数内のaとbを入れ替えてやれば昇順にソートさせることもできます。
結果は以下のようになります。
sorted_str:Z
sorted_str:F
sorted_str:E
sorted_str:D
sorted_str:C
sorted_str:B
sorted_str:A
他にもsortedArrayUsingFunctionやsortedArrayUsingSelector等あります。
数値を降順にソートする場合
NSArray *num_array = [NSArray arrayWithObjects:[NSNumber numberWithInt:0]
, [NSNumber numberWithInt:3]
, [NSNumber numberWithInt:2]
, [NSNumber numberWithInt:4]
, [NSNumber numberWithInt:1]
, [NSNumber numberWithInt:7]
, [NSNumber numberWithInt:10]
, [NSNumber numberWithInt:9]
, [NSNumber numberWithInt:8]
, [NSNumber numberWithInt:5]
, nil];
NSArray *sorted_array = [num_array sortedArrayUsingComparator:^NSComparisonResult(NSNumber *a, NSNumber *b) {
return b.intValue - a.intValue; // ソート
}];
for (NSNumber *aNum in sorted_array) {
NSLog(@"sorted_num:%@", aNum);
}
上記のブロック関数内のaとbを入れ替えてやれば昇順にソートさせることもできます。
結果は以下のようになります。
sorted_num:10
sorted_num:9
sorted_num:8
sorted_num:7
sorted_num:5
sorted_num:4
sorted_num:3
sorted_num:2
sorted_num:1
sorted_num:0
文字を降順にソートする場合
NSArray *str_array = [NSArray arrayWithObjects:@"A", @"E", @"B", @"Z", @"C", @"F", @"D", nil];
sorted_array = [str_array sortedArrayUsingComparator:^NSComparisonResult(NSString *a, NSString *b) {
return [b compare:a]; // ソート
}];
for (NSString *str in sorted_array) {
NSLog(@"sorted_str:%@", str);
}
上記のブロック関数内のaとbを入れ替えてやれば昇順にソートさせることもできます。
結果は以下のようになります。
sorted_str:Z
sorted_str:F
sorted_str:E
sorted_str:D
sorted_str:C
sorted_str:B
sorted_str:A
他にもsortedArrayUsingFunctionやsortedArrayUsingSelector等あります。
2013年3月1日金曜日
Cabochaを呼び出す[mac]
(この記事はiphoneではなく、[mac]のみ対応です。)
日本語の構文解析ツールにCabochaというものがあります。
CabochaにはC言語から呼び出す為のAPIが用意されています。
C言語用のAPIですが、もちろんObjective-Cからも呼び出すことが可能です。
Cabochaがインストールされていることが前提ですが(インストール方法は他のサイトでいろいろ出てきますので・・)、その利用方法はまず、コマンドラインから次のコマンドを入力します。
cabocha-config --cflags
上記でCabochaヘッダのインストールパスを取得できます。
私の環境では
-I/usr/local/include
となりました。
ヘッダのインストールパスをxcodeのBuild Settingsの[SearchPaths]の[Header Search Paths]ところに設定します。その際、-Iは不要なので「/usr/local/include」と設定します。
これでCabochaのAPIを利用する準備ができたので、あとは利用するソースからヘッダを引き込む為に
#import "cabocha.h"
const char *p = [@"これは日本語のテストです。" UTF8String];
const char *result;
// Cabocha起動
char *argv[] = {"cabocha", "-f1"};
c = cabocha_new(2, argv);
result = cabocha_sparse_tostr(c, p);
NSString *strResult = [NSString stringWithCString:result encoding:NSUTF8StringEncoding];
cabocha_destroy(c);
日本語の構文解析ツールにCabochaというものがあります。
CabochaにはC言語から呼び出す為のAPIが用意されています。
C言語用のAPIですが、もちろんObjective-Cからも呼び出すことが可能です。
Cabochaがインストールされていることが前提ですが(インストール方法は他のサイトでいろいろ出てきますので・・)、その利用方法はまず、コマンドラインから次のコマンドを入力します。
cabocha-config --cflags
上記でCabochaヘッダのインストールパスを取得できます。
私の環境では
-I/usr/local/include
となりました。
ヘッダのインストールパスをxcodeのBuild Settingsの[SearchPaths]の[Header Search Paths]ところに設定します。その際、-Iは不要なので「/usr/local/include」と設定します。
これでCabochaのAPIを利用する準備ができたので、あとは利用するソースからヘッダを引き込む為に
#import "cabocha.h"
を書き、以下のようにすればCabochaが呼び出せます。
const char *p = [@"これは日本語のテストです。" UTF8String];
const char *result;
// Cabocha起動
char *argv[] = {"cabocha", "-f1"};
c = cabocha_new(2, argv);
result = cabocha_sparse_tostr(c, p);
NSString *strResult = [NSString stringWithCString:result encoding:NSUTF8StringEncoding];
cabocha_destroy(c);
cabocha_newの1番めの引数はargvの要素数を設定します。
2013年2月27日水曜日
NSDictionary(plist)をバイナリで保存する
NSDictionaryはwriteToFileで簡単に保存できますが、XMLファイルで保存される為、サイズが非常に大きくなってしまう場合があります。
バイナリファイルで保存するには、NSPropertyListSerializationを使って以下のようにします。
// 辞書を生成
NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
// 辞書に追加
[dic setValue:@"1" forKey:@"TEST"];
// ストリームを生成
NSOutputStream *outstream = [NSOutputStream outputStreamToFileAtPath:@"./testDic" append:NO];
[outstream open]; // オープン
// 辞書をバイナリでファイルに保存
[NSPropertyListSerialization writePropertyList:dic toStream:outstream format:NSPropertyListBinaryFormat_v1_0 options:NSPropertyListImmutable error:&error];
if (error != nil) {
NSLog(@"Error:%@", error.description);
}
[outstream close]; // クローズ
保存したものを読み込むには以下のようにします。
dic = [NSDictionary dictionaryWithContentsOfFile:@"./testDic"];
バイナリファイルで保存するには、NSPropertyListSerializationを使って以下のようにします。
// 辞書を生成
NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
// 辞書に追加
[dic setValue:@"1" forKey:@"TEST"];
// ストリームを生成
NSOutputStream *outstream = [NSOutputStream outputStreamToFileAtPath:@"./testDic" append:NO];
[outstream open]; // オープン
// 辞書をバイナリでファイルに保存
[NSPropertyListSerialization writePropertyList:dic toStream:outstream format:NSPropertyListBinaryFormat_v1_0 options:NSPropertyListImmutable error:&error];
if (error != nil) {
NSLog(@"Error:%@", error.description);
}
[outstream close]; // クローズ
保存したものを読み込むには以下のようにします。
dic = [NSDictionary dictionaryWithContentsOfFile:@"./testDic"];
NSArrayとNSDictionaryの検索速度
NSArrayとNSDictionaryを生成して検索速度を比較してみました。
結果、NSDictionaryの検索速度の方が速いようです。
ハッシュ(連想配列)なので当たり前と言えば当たり前ですが。。
// 計測用データ作成
NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0 ; i < 500000; i++ ) {
NSString *val = [NSString stringWithFormat:@"%d", i];
[dic setValue:[NSString stringWithFormat:@"%ld", i] forKey:val];
[array addObject:val];
}
NSLog(@"辞書検索Start");
NSDate *startTime = [NSDate date];
NSString *strTest = @"400000";
NSString *num = [dic valueForKey:strTest]; // 辞書を検索
NSTimeInterval interval = [[NSDate date] timeIntervalSinceDate:startTime];
NSLog(@"辞書検索Stop at:%lf", interval);
if (num) {
NSLog(@"There is %@ in the dictionary", strTest);
} else {
NSLog(@"There is not %@ in the dictionary", strTest);
}
NSLog(@"配列検索Start");
startTime = [NSDate date];
unsigned long idx = [array indexOfObject:strTest];
interval = [[NSDate date] timeIntervalSinceDate:startTime];
NSLog(@"配列検索Stop at:%lf", interval);
if (idx != NSNotFound) {
NSLog(@"There is %@ in the dictionary", strTest);
} else {
NSLog(@"There is not %@ in the dictionary", strTest);
}
NSLog(@"配列検索2Start");
startTime = [NSDate date];
BOOL hasString = [array containsObject:strTest];
interval = [[NSDate date] timeIntervalSinceDate:startTime];
NSLog(@"配列検索2Stop at:%lf", interval);
if (hasString) {
NSLog(@"There is %@ in the dictionary", strTest);
} else {
NSLog(@"There is not %@ in the dictionary", strTest);
}
結果は以下のようになりました。(iPhoneでの計測ではなく、Mac上で計測しました。)
辞書検索Start
辞書検索Stop at:0.000017
here is 400000 in the dictionary
配列検索Start
配列検索Stop at:0.036717
here is 400000 in the dictionary
配列検索2Start
配列検索2Stop at:0.037023
here is 400000 in the dictionary
配列検索ではindexOfObjectによる検索とcontainsObjectによる検索を計測しましたが、どちらも大差は無さそうです。
ちなみに、検索する要素を"400000"から"250000"にすると以下のようになりました。
辞書検索Start
辞書検索Stop at:0.000050
There is 250000 in the dictionary
配列検索Start
配列検索Stop at:0.022836
There is 250000 in the dictionary
配列検索2Start
配列検索2Stop at:0.022718
There is 250000 in the dictionary
ハッシュの検索はほぼ大差ない速度(むしろ若干遅くなっている)ですが、配列の検索では要素位置が上のほうにある為か高速に検索が行えました。
結論としては、
・ハッシュ(NSDictionary)は要素の数や位置に関わらずほぼ同一速度で検索が行える。
・配列(NSArray)は要素数や要素位置に応じて検索速度が変わる
・indexOfObjectとcontainsObjectによる検索は速度に大差なし
というところでしょうか。
結果、NSDictionaryの検索速度の方が速いようです。
ハッシュ(連想配列)なので当たり前と言えば当たり前ですが。。
// 計測用データ作成
NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0 ; i < 500000; i++ ) {
NSString *val = [NSString stringWithFormat:@"%d", i];
[dic setValue:[NSString stringWithFormat:@"%ld", i] forKey:val];
[array addObject:val];
}
NSLog(@"辞書検索Start");
NSDate *startTime = [NSDate date];
NSString *strTest = @"400000";
NSString *num = [dic valueForKey:strTest]; // 辞書を検索
NSTimeInterval interval = [[NSDate date] timeIntervalSinceDate:startTime];
NSLog(@"辞書検索Stop at:%lf", interval);
if (num) {
NSLog(@"There is %@ in the dictionary", strTest);
} else {
NSLog(@"There is not %@ in the dictionary", strTest);
}
NSLog(@"配列検索Start");
startTime = [NSDate date];
unsigned long idx = [array indexOfObject:strTest];
interval = [[NSDate date] timeIntervalSinceDate:startTime];
NSLog(@"配列検索Stop at:%lf", interval);
if (idx != NSNotFound) {
NSLog(@"There is %@ in the dictionary", strTest);
} else {
NSLog(@"There is not %@ in the dictionary", strTest);
}
NSLog(@"配列検索2Start");
startTime = [NSDate date];
BOOL hasString = [array containsObject:strTest];
interval = [[NSDate date] timeIntervalSinceDate:startTime];
NSLog(@"配列検索2Stop at:%lf", interval);
if (hasString) {
NSLog(@"There is %@ in the dictionary", strTest);
} else {
NSLog(@"There is not %@ in the dictionary", strTest);
}
結果は以下のようになりました。(iPhoneでの計測ではなく、Mac上で計測しました。)
辞書検索Start
辞書検索Stop at:0.000017
here is 400000 in the dictionary
配列検索Start
配列検索Stop at:0.036717
here is 400000 in the dictionary
配列検索2Start
配列検索2Stop at:0.037023
here is 400000 in the dictionary
配列検索ではindexOfObjectによる検索とcontainsObjectによる検索を計測しましたが、どちらも大差は無さそうです。
ちなみに、検索する要素を"400000"から"250000"にすると以下のようになりました。
辞書検索Start
辞書検索Stop at:0.000050
There is 250000 in the dictionary
配列検索Start
配列検索Stop at:0.022836
There is 250000 in the dictionary
配列検索2Start
配列検索2Stop at:0.022718
There is 250000 in the dictionary
ハッシュの検索はほぼ大差ない速度(むしろ若干遅くなっている)ですが、配列の検索では要素位置が上のほうにある為か高速に検索が行えました。
結論としては、
・ハッシュ(NSDictionary)は要素の数や位置に関わらずほぼ同一速度で検索が行える。
・配列(NSArray)は要素数や要素位置に応じて検索速度が変わる
・indexOfObjectとcontainsObjectによる検索は速度に大差なし
というところでしょうか。
登録:
投稿 (Atom)