今天我在尝试使用Objective-C的块(Block),所以我想自己写一些函数式集合方法,来扩展NSArray,这些方法在其他语言中都有看到:
select: 方法将返回一个新数组,其中只包含原始数组中当作参数传递给块时,块返回 YES 的项目。
最后,flattenedArray 方法遍历数组的项目。如果项目是一个数组,则递归调用 flattenedArray 并将结果添加到结果集中。如果项目不是数组,则将其添加到结果集中。当一切结束时,返回结果集。
现在我有了一些基础设施,我需要一个测试案例。 我决定查找系统应用程序目录中的所有软件包文件。下面是我想出的代码:
@interface NSArray (FunWithBlocks)
- (NSArray *)collect:(id (^)(id obj))block;
- (NSArray *)select:(BOOL (^)(id obj))block;
- (NSArray *)flattenedArray;
@end
collect: 方法以一个 block 作为参数,对数组中的每个元素调用该 block,并期望使用该元素执行某些操作并返回结果。结果是所有这些结果的集合。(如果 block 返回 nil,则不会将任何内容添加到结果集中。)select: 方法将返回一个新数组,其中只包含原始数组中当作参数传递给块时,块返回 YES 的项目。
最后,flattenedArray 方法遍历数组的项目。如果项目是一个数组,则递归调用 flattenedArray 并将结果添加到结果集中。如果项目不是数组,则将其添加到结果集中。当一切结束时,返回结果集。
现在我有了一些基础设施,我需要一个测试案例。 我决定查找系统应用程序目录中的所有软件包文件。下面是我想出的代码:
NSArray *packagePaths = [[[NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES) collect:^(id path) { return (id)[[[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil] collect:^(id file) { return (id)[path stringByAppendingPathComponent:file]; }]; }] flattenedArray] select:^(id fullPath) { return [[NSWorkspace sharedWorkspace] isFilePackageAtPath:fullPath]; }];
没错 - 这是一行代码,看起来很可怕。我尝试了几种方法来添加换行和缩进以使其更加清晰,但仍感觉真正的算法在所有噪音中都丢失了。虽然我不知道是语法问题还是函数式使用方面的相对经验不足造成了问题。
为了对比,我决定采用“老派的方法”,只使用循环:
NSMutableArray *packagePaths = [NSMutableArray new];
for (NSString *searchPath in NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES)) {
for (NSString *file in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:searchPath error:nil]) {
NSString *packagePath = [searchPath stringByAppendingPathComponent:file];
if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:packagePath]) {
[packagePaths addObject:packagePath];
}
}
}
在我看来,这个版本更易于编写,同时也更容易阅读。
我想这可能是一个不好的例子,但在我看来,这似乎是使用块的合法方式。 (我错了吗?) 我是否忽略了如何使用块来编写或组织Objective-C代码以清除并使其比循环版本更清晰的内容?