1 つのアプローチは、再帰を使用することです。このアプローチは最も安価ではありませんが、構造の深さが変更されたり、コンパイル時に不明な場合に柔軟に対応できるという利点があります。
擬似コード:
integer count ( collection , maxDepth )
return recursiveCount ( collection , 0 , maxDepth )
end
integer recursiveCount ( collection , currentDepth , maxDepth )
integer count = 0
if collection is array
count += recursiveCountArray ( collection , currentDepth , maxDepth )
else if object is dictionary
count += recursiveCountDictionary ( collection.values , currentDepth , maxDepth )
else
count += 1
return count
end
integer recursiveCountArray ( array , currentDepth , maxDepth )
integer count = 0
if currentDepth < maxDepth
for ( object in array )
count += recursiveCount( object )
else
count += array.count
return count
end
このmaxDepth
パラメーターにより、必要以上の作業が行われないことが保証されます (@RamyAlZuhouri がコメントで提案したように、配列を反復処理して反復ごとにカウントを 1 ずつ増やすのではなく、単に maxDepth のときに配列のカウントを返すだけです)。
Objective-C では、これは C 関数で実現できます。
static NSUInteger _PFXRecursiveCount(id collection, NSUInteger currentDepth, NSUInteger maxDepth);
static NSUInteger _PFXRecursiveCountArray(id array, NSUInteger currentDepth, NSUInteger maxDepth);
NSUInteger PFXRecursiveCount(id collection, NSUInteger maxDepth)
{
return _PFXRecursiveCount(collection, 0, maxDepth);
}
NSUInteger _PFXRecursiveCount(id collection, NSUInteger currentDepth, NSUInteger maxDepth)
{
NSUInteger count = 0;
if ([collection isKindOfClass:[NSArray class]]) {
count = _PFXRecursiveCountArray(collection, currentDepth, maxDepth);
} else if ([collection isKindOfClass:[NSDictionary class]]) {
NSDictionary *dictionary = (NSDictionary *)collection;
count = _PFXRecursiveCountArray(dictionary.allValues, currentDepth, maxDepth);
} else {
count = 1;
}
return count;
}
NSUInteger _PFXRecursiveCountArray(NSArray *array, NSUInteger currentDepth, NSUInteger maxDepth)
{
NSUInteger count = 0;
if (currentDepth < maxDepth) {
for (id object in array) {
count += _PFXRecursiveCount(object, currentDepth + 1, maxDepth);
}
} else {
count += array.count;
}
return count;
}
ここPFX
は、プロジェクトで使用されている適切なプレフィックスに置き換えられます。ヘッダーでのみPFXRecursiveCount
宣言されます。
または、これはブロックで実現できます。
typedef NSUInteger (^RecursiveCountArrayBlock)(NSArray *, NSUInteger, NSUInteger);
typedef NSUInteger (^RecursiveCountBlock)(id, NSUInteger, NSUInteger);
__block __weak RecursiveCountArrayBlock _weakRecursiveCountArray = nil;
__block __weak RecursiveCountBlock _weakRecursiveCount = nil;
NSUInteger (^_recursiveCount)(id, NSUInteger, NSUInteger) = ^(id collection, NSUInteger currentDepth, NSUInteger maxDepth) {
NSUInteger count = 0;
if ([collection isKindOfClass:[NSArray class]]) {
count = _weakRecursiveCountArray(collection, currentDepth, maxDepth);
} else if ([collection isKindOfClass:[NSDictionary class]]) {
NSDictionary *dictionary = (NSDictionary *)collection;
count = _weakRecursiveCountArray(dictionary.allValues, currentDepth, maxDepth);
} else {
count = 1;
}
return count;
};
_weakRecursiveCount = _recursiveCount;
NSUInteger (^_recursiveCountArray)(id, NSUInteger, NSUInteger) = ^(NSArray *array, NSUInteger currentDepth, NSUInteger maxDepth) {
NSUInteger count = 0;
if (currentDepth < maxDepth) {
for (id object in array) {
count += _weakRecursiveCount(object, currentDepth + 1, maxDepth);
}
} else {
count += array.count;
}
return count;
};
_weakRecursiveCountArray = _recursiveCountArray;
NSUInteger (^recursiveCount)(id, NSUInteger) = ^(id collection, NSUInteger maxDepth) {
return _recursiveCount(collection, 0, maxDepth);
};
__weak
__block
変数 (_weakRecursiveCountArray
および) を使用_weakRecursiveCount
すると、ブロック自体からブロックへの強い参照を避けることができます。(注: iOS 5 および 10.7 より前では、__weak
を に置き換える必要があります__unsafe_unretained
。) typedef を使用すると、誤った警告を回避できます (「'__weak' は、objective-c オブジェクトまたはブロック ポインター型にのみ適用されます。ここでの型は 'NSUInteger' です。 (別名「unsigned long」)")。