是否存在像C++ STL的`transform`这样的Objective-C算法?

pes*_*che 9 arrays algorithm objective-c

我的目标是拥有一个包含特定扩展名的所有文件名但没有扩展名的数组.

有一个优雅的解决方案,使用谓词过滤器获取特定扩展的所有文件名,以及如何将路径拆分为文件名和扩展名的说明,但要合并它们,我必须编写一个循环(不是很糟糕,但也不优雅).

是否有一种方法使用Objective-C(可能类似于谓词机制)将一些函数应用于数组的每个元素,并将结果放在第二个数组中,就像transformC++ STL 的算法一样?

我想写的:

// let's pretend 'anArray' was filled by querying the filesystem and not hardcoded
NSArray* anArray = [[NSArray alloc] initWithObjects:@"one.ext", @"two.ext", nil];

// that's what I liked to write (pseudo code)
NSArray* transformed = [anArray transform: stringByDeletingPathExtension];

// Yuji's answer below proposes this (which may be as close as you can get
// to my wish with Objective C)
NSArray* transformed = [anArray my_arrayByApplyingBlock:^(id x){
                           return [x stringByDeletingPathExtension];
                       }];
Run Code Online (Sandbox Code Playgroud)

Nea*_*rdt 11

实际上,有一种非常简单的方法.它自2003年以来一直存在,而且名字很差.

NSArray *array = [NSArray arrayWithObjects:@"one.ext", @"two.ext", nil];

// short solution
NSArray *transformed = [array valueForKey:@"stringByDeletingPathExtension"];

// long solution (more robust against refactoring)
NSString *key = NSStringFromSelector(@selector(stringByDeletingPathExtension));
NSArray *transformed = [array valueForKey:key];
Run Code Online (Sandbox Code Playgroud)

两者都产生输出:

(
    one,
    two
)
Run Code Online (Sandbox Code Playgroud)


Yuj*_*uji 9

这是一个名为Cocoa的高级订单消息的主题,并且由网络上的许多人开发.从这里开始,尝试谷歌搜索更多.他们添加了一个类别方法,NSArray以便您可以这样做

NSArray*transformed=[[anArray map] stringByDeletingPathExtension];
Run Code Online (Sandbox Code Playgroud)

这个想法如下:

  • [anArray map]创建一个临时对象(比如说hom)
  • hom 收到消息 stringByDeletingPathExtension
  • hom 将消息重新发送给所有元素 anArray
  • hom 收集结果并返回结果数组.

如果你只想快速转换,我会定义一个类别方法:

@interface NSArray (myTransformingAddition)
-(NSArray*)my_arrayByApplyingBlock:(id(^)(id))block;
@end

@implementation NSArray (myTransformingAddition)
-(NSArray*)my_arrayByApplyingBlock:(id(^)(id))block{
     NSMutableArray*result=[NSMutableArray array];
     for(id x in self){
          [result addObject:block(x)];
     }
     return result;
}
@end
Run Code Online (Sandbox Code Playgroud)

那你可以做

NSArray* transformed=[anArray my_arrayByApplyingBlock:^id(id x){return [x stringByDeletingPathExtension];}];
Run Code Online (Sandbox Code Playgroud)

请注意^ return-type (arguments) { ...}创建块的构造.返回类型可以省略,并且clang在猜测它时非常聪明,但是gcc它非常严格,需要在某个时候指定.(在这种情况下,它是从返回的return语句中猜到[x stringBy...]NSString*.所以GCC猜测了块的返回类型NSString*而不是idGCC认为是不兼容的,因此出现了错误.)

在OS X Leopard或iOS 3上,您可以使用PLBlock来支持块.我个人的主观意见是,关心新软件的人通常会升级到最新的操作系统,所以支持最新的操作系统应该没问题.支持较旧的操作系统不会使您的客户增加两倍......

这就是说,已经有一个很好的开源框架,它完成了我上面所说的一切.请参阅此处的讨论,特别是那里链接的FunctionalKit.


更多补充:实际上很容易实现你的伪代码[array transform:stringByDeletingPathExtension].

@interface NSArray (myTransformingAddition)
-(NSArray*)my_transformUsingSelector:(SEL)sel;
@end

@implementation NSArray (myTransformingAddition)
-(NSArray*)my_transformUsingSelector:(SEL)sel;{
     NSMutableArray*result=[NSMutableArray array];
     for(id x in self){
          [result addObject:[x performSelector:sel withObject:nil]];
     }
     return result;
}
@end
Run Code Online (Sandbox Code Playgroud)

然后你可以使用它如下:

NSArray*transformed=[array my_transformUsingSelector:@selector(stringByDeletingPathExtension)];
Run Code Online (Sandbox Code Playgroud)

但是我不喜欢它; 您需要在数组中的对象上定义一个方法才能使用此方法.例如,如果NSString没有您想要作为方法执行的操作,在这种情况下您会做什么?您需要先NSString通过类别添加它:

@interface NSString (myHack)
-(NSString*)my_NiceTransformation;
@end

@implementation NSString (myHack)
-(NSString*)my_NiceTransformation{
   ... computes the return value from self ...
   return something;
}
@end
Run Code Online (Sandbox Code Playgroud)

然后你可以使用

NSArray*transformed=[array my_transformUsingSelector:@selector(my_NiceTransformation)];
Run Code Online (Sandbox Code Playgroud)

但它往往非常冗长,因为你需要先在其他地方定义方法.我更喜欢在呼叫站点提供我想要直接操作的内容,如

NSArray*transformed=[array my_arrayByApplyingBlock:^id(id x){
   ... computes the return value from x ...
   return something;    
}];
Run Code Online (Sandbox Code Playgroud)

最后,永远不要添加不以类似前缀my_或其他内容开头的类别方法.例如,将来Apple可能会提供一个很好的方法transform,它可以完全满足您的需求.但是如果你已经transform在类别中调用了一个方法,那将导致一个未定义的行为.事实上,Apple可能已经在课堂上采用了私有方法.