代码之家  ›  专栏  ›  技术社区  ›  polygenelubricants

-1是一个神奇的数字吗?反模式?代码气味?引文和当局指导方针[副本]

  •  12
  • polygenelubricants  · 技术社区  · 15 年前

    可能重复:
    Constant abuse?

    我见过 -1 在各种API中使用,最常见的是在搜索具有零基索引的“集合”时,通常用于指示“未找到”索引。这个“有效”是因为 - 1 从来不是一个合法的索引。似乎任何负数都能起作用,但我认为 - 1 几乎总是被用作某种(不成文的?)公约。

    我希望至少现在将范围限制到Java。我的问题是:

    • 关于使用sun的官方说法是什么 - 1 像这样的“特殊”返回值?
    • 关于这个问题,从詹姆斯·高斯林、Josh Bloch、甚至其他爪哇以外的权威人物那里有什么引证?
    • 过去关于这个问题的一些重要讨论是什么?
    10 回复  |  直到 15 年前
        1
  •  11
  •   mdma    15 年前

    这是语言中的一个常见习惯用法,其中类型不包括范围检查。“越界”值用于指示多个条件之一。在这里,返回值表示两件事:1)找到字符,2)找到字符的位置。 使用-1 not found 非负指数 found 简洁地将这两个值编码为一个值,事实是 not-found 不需要返回索引。

    在具有严格范围检查的语言中,例如ADA或Pascal,该方法可以实现为(伪代码)

       bool indexOf(c:char, position:out Positive);
    

    Positive 是int的子类型,但仅限于非负值。

    这将“找到/未找到”标志与位置分开。位置作为out参数提供-本质上是另一个返回值。它也可以是一个输入输出参数,从给定位置开始搜索。此处不允许使用-1来表示未找到,因为它违反了对正类型的范围检查。

    Java中的替代方案是:

    • 抛出一个例外:这不是一个好的选择在这里,因为没有找到一个字符不是一个例外的条件。
    • 将结果分成若干种方法,例如 boolean indexOf(char c); int lastFoundIndex(); . 这意味着对象必须保持状态,除非状态存储在线程本地存储中,或者使用同步,否则在并发程序中不会工作——所有相当大的开销。
    • 返回位置并分别找到标记:例如 boolean indexOf(char c, Position pos) . 在这里,创建位置对象可能被视为不必要的开销。
    • 创建多值返回类型

    class FindIndex {
       boolean found;
       int position;
    }
    
    FindIndex indexOf(char c);
    

    尽管它清楚地分离返回值,但它会受到对象创建开销的影响。其中一些可以通过 FindIndex 作为参数,例如

    FindIndex indexOf(char c, FindIndex start);
    

    顺便说一下,多个返回值将是Java(橡树)的一部分,但是在1之前被切断以释放时间。詹姆斯·高斯林 says 他希望他们被包括在内。它仍然是 wished-for feature .

    我的看法是,使用magic值是一种在单个返回值中对多值结果(标志和值)进行编码的实用方法,而不需要过多的对象创建开销。

    但是,如果使用magic值,那么如果它们在相关的API调用之间保持一致,就可以更好地使用它们。例如,

       // get everything after the first c
       int index = str.indexOf('c');
       String afterC = str.substring(index);
    

    Java在这里很短,因为在调用中使用-1。 substring 将导致 IndeOutOfBoundsException . 相反,如果将负值视为从字符串末尾开始,则当使用-1调用时,子字符串返回“”可能更为一致。对于错误条件的魔法值的批评者说返回值可以被忽略(或者假设为正)。一致的API以一种有用的方式处理这些魔力值,将减少对-1的检查,并允许使用更干净的代码。

        2
  •  4
  •   Stephen C    15 年前

    -1是一个神奇的数字吗?

    在这种情况下,不是真的。没有什么特别的 -1 …除此之外,由于它是负的,所以保证它是一个无效的索引值。

    反模式?

    不。要符合反模式的条件,这个成语需要有一些有害的东西。我觉得使用没有什么有害的 - 1 这种方式。

    代码气味?

    同上。(可以说,使用命名常量比使用空常量更好) - 1 字面意义的。但我不认为这就是你要问的问题,而且它也不算是“代码味道”,依我看。)

    权威机构的报价和指导方针

    我不知道。但是,我会注意到这个“设备”在不同的标准类中使用。例如, String.indexOf(...) 收益率 - 1 表示找不到字符或子字符串。


    就我而言,这只是一个在某些情况下有用的“算法装置”。我相信如果你回顾一下文献,你会看到一些使用 - 1 (或) 0 对于只有一个基数组的语言),这种方式可以追溯到1960年代和以前。

    选择 - 1 而不是其他一些负面数字只是个人品味的问题,在这种情况下(IMO)不值得分析。


    对于返回的方法来说,这可能是一个坏主意 - 1 (或其他值)指示错误而不是引发异常。但是,这里的问题不是返回的值,而是方法要求调用者显式测试错误的事实。

    另一方面,如果“条件”由 - 1 (或其他)是 “错误”/“异常情况”,则返回特殊值既合理又适当。

        3
  •  3
  •   scunliffe    15 年前

    Java和JavaScript的使用 -1 当找不到索引时。因为索引总是 0-n 这似乎是一个很明显的选择。

    //JavaScript
    var url = 'example.com/foo?bar&admin=true';
    if(url.indexOf('&admin') != -1){
      alert('we likely have an insecure app!');
    }
    

    我发现这种方法(在扩展数组类型元素 .indexOf() 方法)非常正常。

    另一方面,您可以尝试使用php方法,例如 strpos() 但是imho它会变得混乱,因为有多个返回类型(未找到时返回false)

        4
  •  2
  •   mikera    15 年前

    -1作为一个返回值有点难看,但是必要的。表示“未找到”情况的替代方案是更糟的:

    • 你可以提出一个例外,但是 这不理想,因为例外 最好用来表示意外 需要某种形式的 恢复或传播失败。不是 查找子字符串的匹配项 实际上是意料之中的。阿尔索 异常引发具有显著的 性能惩罚。

    • 你可以使用复合结果 对象(找到,索引),但此 需要对象分配和 更复杂的代码 调用方检查结果。

    • 你可以把两个分开 函数调用contains和indexof-但这是 对打电话的人来说又很麻烦 也会导致性能下降 因为两个电话都是O(N)和 需要对 字符串。

    就我个人而言,我从不喜欢引用-1常量:我对“未找到”的测试总是类似于:

    int i = someString.indexOf("substring");
    if (i>=0) {
      // do stuff with found index
    } else {
      // handle not found case
    }
    
        5
  •  1
  •   PeterMmm    15 年前

    为定义一个最终类变量是很好的实践 全部的 代码中的常量值。 但一般都接受使用0、1、-1、“”(空字符串)而不进行显式声明。

        6
  •  1
  •   Thorbjørn Ravn Andersen    15 年前

    这是从C继承的,只有一个基元值可以返回。在爪哇中,还可以返回单个对象。

    因此,对于新代码,返回一个basetype的对象,其子类型指示要与instaceof一起使用的问题,或者引发“未找到”异常。

    对于现有的特殊值,使-1相应地成为代码名中的常量-找不到-这样读者就可以不必检查javadocs就知道其含义。

        7
  •  1
  •   Community CDub    8 年前

    与…相同的做法 null 适用于 -1 . 它已经讨论过很多次了。

    例如 Java api design - NULL or Exception

        8
  •  1
  •   Adam Houldsworth    15 年前

    它的使用是因为它是您在基于0的数组中遇到的第一个无效值。正如您所知,并非所有类型都可以包含空值或空值,因此需要“something”来表示空值。

    我想说它不是官方的,它只是成为惯例(不成文),因为它对形势非常敏感。就个人而言,我也不认为这是个问题。API的设计也取决于作者,但是 guidelines can be found online .

        9
  •  1
  •   back2dos    15 年前

    据我所知,这些值称为sentinel值,尽管大多数常见的定义与此场景稍有不同。

    Java等语言选择不支持通过引用(我认为这是一个好主意),因此当单个参数的值是可变的时,传递给函数的变量仍然不受影响。因此,只能有一个类型的返回值。所以您要做的是选择一个有效类型的无效值,并返回它以传输附加的语义,因为返回值实际上不是操作的返回值,而是一个特殊的信号。

    现在我想,最干净的方法是 contains 和一个 indexOf 方法,如果您请求的元素不在集合中,则第二个方法将引发异常。为什么?因为人们希望以下内容是正确的:

    someCollection.objectAtIndex(someCollection.indexOf(someObject)) == someObject
    

    你可能得到的是一个例外,因为 -1 是超出界限的,而这种似是而非的关系的实际原因是 someObject 不是的元素 someCollection ,这就是内部调用应该引发异常的原因。

    现在,尽管如此,它还是有两个主要缺陷:

    • 通常这两个操作都会花费您O(N)(除非您在集合中有一个反向映射),所以如果只做一个,您会更好。
    • 这真的很冗长。

    最后,由你来决定。这是一个哲学问题。我将其称为“语义攻击”,以牺牲健壮性来实现短和快。你的电话;

    尔兹
    后台操作系统

        10
  •  1
  •   Xaqron    15 年前

    就像为什么51%意味着公司股东之间的一切,因为它是最接近的,也有意义,而不是-2或-3…

    推荐文章