【问题标题】:Obj-C: if statements and NSString comparison functions for efficiency and readabilityObj-C:if 语句和 NSString 比较函数以提高效率和可读性
【发布时间】:2013-01-30 18:00:24
【问题描述】:

什么是高效编码的最佳方法,因为这将被递归调用,并且创建大量对象是不好的。 +*-/, sin,cos,sqrt,pi and x,y,a,b 应该放在三个单独的 if statements 中,不像现在所有东西都是分开的。同样为了可读性,制作包含 4 isEqualToString 的 if 语句也不是很好。

    NSString *operation = topOfStack;
    if ([operation isEqualToString:@"+"]) {

    } else if ([@"*" isEqualToString:operation]) {

    } else if ([operation isEqualToString:@"-"]) {

    } else if ([operation isEqualToString:@"/"]) {


    } else if ([operation isEqualToString:@"sin"]) {

    } else if ([operation isEqualToString:@"cos"]) {

    } else if ([operation isEqualToString:@"sqrt"]) {

    } else if ([operation isEqualToString:@"pi"]) {


    } else if ([operation isEqualToString:@"x"]) {

    } else if ([operation isEqualToString:@"y"]) {

    } else if ([operation isEqualToString:@"a"]) {

    } else if ([operation isEqualToString:@"b"]) {

    }

【问题讨论】:

  • 老实说,在 Objective-c 中没有很好的方法来做到这一点。理想的方法是使用 switch 语句,但不幸的是,objc 中的 switch 语句不支持对象。
  • Re:“创建大量对象不好”——这段代码都没有创建任何对象。
  • 说到 NSStrings 上的 switch 语句,你为什么不看看我为在对象上做 switch 做的这个头库:stackoverflow.com/questions/4224495/…

标签: objective-c if-statement nsstring


【解决方案1】:

我认为没有最佳方法可以做到这一点,但这里有另一种选择来补充已经给出的答案。

创建一个将您的操作名称(sin、sqrt、-)映射到选择器的 NSDictionary,如下所示:

NSDictionary *operations = @{
    @"sin": [NSValue valueWithPointer:@selector(operationSin:)],
    /* other operations here */
};

那么你的 switch 语句就变成了一个查找。

NSString *operation = topOfStack;
if(operations[operation]) {
    SEL op = [operations[operation] pointerValue];
    [self performSelector:op withObject:value];  /* Or some approximation thereof */
}
else {
    /* Default action for unknown operation */
}

或者,您可以在识别的操作上创建一个枚举,使用您的 NSDictionary 将操作名称映射到一个 NSNumber 装箱您的枚举值。

typedef NS_ENUM(NSUInteger, OperationType) {
    OperationTypeSin = 0L,
    OperationTypeSqrt,
    /* and so on */
    OperationTypeUnknown
};

/* And later...  */
NSDictionary *operations = @{
    @"sin": @(OperationTypeSin),
    /* you get the idea */
};

/* Finally ... */
NSString *operation = topOfStack;
OperationType opType = [operations[operation] unsignedIntegerValue];
switch(opType) {
    case OperationTypeSin:
        /* Much cleaner, and type-safe too */
}

【讨论】:

  • 我想你在想[self performSelector:op withObject:value]
  • 这就是我在午休时间回答的结果。谢谢。
【解决方案2】:

准备工作:
首先将 NSString 操作转换为 int hashcode,将该 hashcode 存储在 #define 或常数。

代码:

1) 将 NSString 操作转换为 int hashcode (tokenId)。
2) 然后对该 tokenId 执行 switch 语句。

int token = operationToToken(operation);
switch (token) {
case: OP_MINUS: break;
case: OP_SIN: break;
case: OP_COS: break;

}

【讨论】:

  • 哈希可以(并且确实)发生冲突,并且并非真正用于这样的事情。您不考虑的另一件事是区分大小写,如果有用户输入,则应注意这一点。不错的解决方案,在 C 语言中确实没有很多“好的”答案,但我确信可以做得更好。
  • 您可以检查它们是否发生冲突,或者不是因为您的字母表有限,比如说 20 个操作字符串。如果它们发生冲突,则创建一个更好的散列函数,例如,通过将每个字母是基数为 26 的数字 0-26 求和。检查正确的输入(toLowercase() 可以提前完成,问题是为了提高效率(巨大) 递归调用次数。
  • 这在理论上很好,但是您已将问题从“比较字符串以执行操作”更改为“编写足够的哈希函数”,如果如果添加了足够多的操作。
  • 如果你需要性能,那么这是一种方法,如果你不需要它,它会保留字符串比较,否则。然而,当然应该有一个单元测试来检查你的整个字母表是否返回一个唯一的哈希码。
【解决方案3】:

首先,可能会退回到 C 字符串,然后利用 sincossqrtpiabxy(几乎)都以不同的字符开头:

const char *s = [operation UTF8String];
switch (s[0]) {
case '+':
    // addition
    break;
case '-':
    // subtraction
    break;
case 's':
    // sine or sqrt
    switch (s[1]) {
    case 'q':
        // sqrt
        break;
    case 'i':
        // sine
        break;
    }
    break;
case 'c':
    // cosine
    break;
// et cetera...
default:
    // not found
}

【讨论】:

  • 我不建议这样做,当有人试图做 arc-cos 或 arc-sine 时,你会怎么做?
  • @RichardJ.RossIII 我知道,我知道...我刚刚注意到我们有sqrtsin
  • 它会起作用 - 但正如我上面的海报所说 - 如果我想用更多操作扩展计算器,我可能不得不重写整个方法
  • @TomLilletveit 你仍然可以strcmp(),任何体面的编译器都可以将它内联为短字符串。
  • @H2CO3 我了解您要对更新做什么,但老实说,它的可读性不如 OP 的版本。我不应该深入挖掘嵌套的 switch 语句来弄清楚发生了什么。
【解决方案4】:

如果您使用的是 Objective-C,那么您可以使用一些巧妙命名的选择器来做到这一点。

首先,创建一个对象来封装Operation

然后,创建一个-initWithOperation:(NSString *)op 方法。在此方法中,将任何符号操作(*+ 等)转换为唯一的字符串标识符(仅限字母)。

然后,您可以调用 NSSelectorFromString(stringOp),但如果您希望方法具有参数,则必须自己附加 : 字符。

获得选择器后,您可以使用-performSelector:+performSelector: 执行选择器,或手动调用选择器。

如果您将每个选择器命名为与操作相同的名称,则不需要任何 ifswitch 语句,但您必须处理发送无效选择器的情况。

例如:

@interface Operation : NSObject

- (id)initWithOperation:(NSString *)op;
- (void)performOperationWithValue:(float)value;
+ (float)sin:(float)value;
// ... Other operations
@property (nonatomic, copy) NSString *theOperation;

@end

@implementation Operation
- (id)initWithOperation(NSString *)op {
    self = [super init];
    if (self) {
        // Convert symbols to unique strings
        theOperation = [[NSString alloc] initWithString:op];
    }
    return self;
}

- (void)performOperationWithValue:(float)value {
    NSString *withOneParam = [self.theOperation stringByAppendingString:@":"];
    SEL sel = NSSelectorFromString(withOneParam);
    [Operation performSelector:withOneParam withObject:[NSNumber numberWithFloat:value]];
}

// Class methods are the actual operation implementation...

在上面的示例中,我假设您已将@"sin" 传递给Operation 对象。这只能在 Objective-C 中实现,因为您可以利用命名选择器。

【讨论】:

    【解决方案5】:

    您可以为每个符号分配一个数字,并可以实现一个开关盒。

    【讨论】:

    • 虽然是的,您可以这样做,但从长远来看,这会让事情变得非常丑陋,并且在这一点上,我认为从长远来看,对对象使用模拟开关最终会得到更具可读性的代码,喜欢这个:stackoverflow.com/questions/4224495/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-21
    • 1970-01-01
    • 2011-07-11
    • 2018-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多