【问题标题】:Can you write any algorithm without an if statement?你能写出没有 if 语句的算法吗?
【发布时间】:2010-12-28 14:02:34
【问题描述】:

这个网站激起了我的幽默感 - http://www.antiifcampaign.com/,但多态性是否可以在您使用 if 语句的所有情况下工作?

【问题讨论】:

  • 我认为标题有点误导。我建议“你能避免在每种情况下都使用适当的 OO 进行显式类型检查吗?”
  • 这听起来有点像“物体健美操”:dubroy.com/blog/…
  • 如果是新的 GOTO ...就像第二个可能被滥用一样,第一个也可以。让我想起了猎杀者……见多识广的人害怕他们不知道的东西……
  • @Idigas:你是怎么打绞盘的?
  • @RCIX:你听着叮当声……叮当声……叮当声……然后寻找被拉起的东西。 ;)

标签: oop


【解决方案1】:

虽然与 OOP 无关:在 Prolog 中,编写整个应用程序的唯一方法是不使用 if 语句。

【讨论】:

  • 这是我的噩梦 :( ...虽然有点有趣 :)
【解决方案2】:

是的,实际上,您可以拥有一种本身没有“if”而只允许“while”语句的图灵完备语言:

http://cseweb.ucsd.edu/classes/fa08/cse200/while.html

对于 OO 设计,在某些情况下使用继承模式而不是基于类型字段的切换是有意义的……但这并不总是可行或必然需要的。

@ennukiller:条件句只是语法糖的问题:

if (test) body;     is equivalent to    x=test; while (x) {x=nil; body;}

if-then-else 有点冗长:

if (test) ifBody; else elseBody;

is equivalent to

x = test; y = true;
while (x) {x = nil; y = nil; ifBody;}
while (y) {y = nil; elseBody;}

原始数据结构是列表的列表。如果它们是相同长度的列表,您可以说 2 个标量相等。您将使用头/尾运算符同时循环它们并查看它们是否在同一点停止。

当然,这一切都可以用宏包裹起来。

最简单的图灵完备语言大概是iota。它仅包含 2 个符号('i' 和 '*')。

【讨论】:

  • 这种语言的条件结构是什么?你如何比较原语?这就是你的 if(或 switch)语句!
  • 纯oo语言中没有“原语”
【解决方案3】:

Haskell 甚至没有 if 语句,它是纯函数式的。 ;D

【讨论】:

  • 是的:isZero x = if x == 0 then True else False
  • 这是一个if表达式,完全等价于cond True a b = a; cond False a b = b,和smalltalk的ifTrue:ifFalse:方法没什么区别
  • 从所有意图和目的来看,它都是一个 if 语句。
【解决方案4】:

多态性的思想是调用一个对象而不首先验证该对象的类。
这并不意味着根本不应该使用 if 语句。你应该避免写

if (object.isArray()) {
  // Code to execute when the object is an array.
} else if (object.inString()) {
  // Code to execute if the object is a string.
}

【讨论】:

    【解决方案5】:

    该网站反对使用 if 语句来检查对象是否具有特定类型。这与if (foo == 5) 完全不同。像if (foo instanceof pickle) 这样使用ifs 是不好的。另一种方法是使用多态性来促进封装,使代码更容易调试、维护和扩展。

    一般而言,反对ifs(根据条件做某件事)将一无所获。请注意这里的所有其他答案仍然如何做出决定,那么真正的区别是什么?

    解释多态背后的原因

    以这种情况为例:

    void draw(Shape s) {
        if (s instanceof Rectangle)
            //treat s as rectangle
        if (s instanceof Circle)
            //treat s as circle
    }
    

    如果您不必担心对象的特定类型,概括对象的处理方式,那就更好了:

    void draw(Shape s) {
        s.draw();
    }
    

    这会将如何绘制形状的逻辑转移到形状类本身中,因此我们现在可以将所有形状视为相同。这样,如果我们想添加一种新的形状,我们所要做的就是编写类并给它一个draw方法,而不是修改整个程序中的每个条件列表。

    这个想法在今天的编程中无处不在,接口的整个概念都是关于多态性的。 (Shape 是定义特定行为的接口,允许我们处理在我们的方法中实现 Shape 接口的任何类型。)动态编程语言更进一步,允许我们将任何支持必要操作的类型传递给方法。你觉得哪个更好看? (Python风格的伪代码)

    def multiply(a,b):
        if (a is string and b is int):
            //repeat a b times.
        if (a is int and b is int):
            //multiply a and b
    

    或使用多态性:

    def multiply(a,b):
        return a*b
    

    您现在可以使用支持 * 运算符的任何 2 种类型,允许您将方法用于尚未创建事件的类型。

    请参阅 polymorphismwhat is polymorhism

    【讨论】:

    • 这对自我鞭笞的程序员来说是件好事。
    • +1 用于回答基本问题,而不仅仅是回答标题。
    • 不过,该网站的名称另有说明。
    • 您的答案已经得到了很好的解释,但 OOP 不仅仅是避免使用类型标签。大多数 GoF 模式都是关于避免 IF 并降低圈复杂度,即避免其他类型的 IF
    • 很好的答案! Ifs 的速度要快得多,但在宏大的计划中,您可以很好地反驳它们的边际优势。
    【解决方案6】:

    Smalltalk,被认为是“真正的”面向对象语言,没有“if”语句,也没有“for”语句,没有“while”语句。还有其他示例(例如 Haskell),但这是一个很好的示例。

    引用Smalltalk has no “if” statement:

    有些观众可能在想 这是证实他们的证据 怀疑 Smalltalk 很奇怪, 但我要告诉你的是 这个:

    “if”语句在面向对象语言中是可憎的。

    为什么?好吧,一个面向对象的语言是组成的 类、对象和方法,以及 “如果”语句不可避免地没有 那些。你不能写“如果”在 OO方式。它不应该存在。 条件执行,就像一切 否则,应该是一种方法。一种方法 什么?布尔值。

    现在,有趣的是,在 Smalltalk 中, Boolean 有一个方法叫做 ifTrue:ifFalse:(这个名字看起来 现在很奇怪,但忽略它 现在)。它在布尔值中是抽象的,但是 Boolean 有两个子类:True 和 错误的。该方法通过两个块 的代码。在 True 中,该方法很简单 运行真实案例的代码。在 假,它运行假的代码 案子。这是一个希望 解释:

    (x >= 0) ifTrue: [
    'Positive'
    ] ifFalse: [
    'Negative'
    ]
    

    您应该能够看到 ifTrue: 和 ifFalse:在里面。别担心 他们没有在一起。

    表达式 (x >= 0) 的计算结果为 对或错。说是真的,那我们 有:

    true ifTrue: [
    'Positive'
    ] ifFalse: [
    'Negative'
    ]
    

    我希望很明显 这将产生“积极”。

    如果它是假的,我们会有:

    false ifTrue: [
    'Positive'
    ] ifFalse: [
    'Negative'
    ]
    

    这会产生“负面”。

    好的,就是这样。怎么回事 很棒吗?那么,在什么其他 语言你能做到吗?更多的 说真的,答案是 在这方面没有任何特殊情况 语言。一切都可以在一个 OO方式,一切都在一个 OO方式。

    我绝对推荐阅读整篇文章以及来自同一作者的Code is an object

    【讨论】:

    • 这与 if 的所有意图和目的并没有什么不同
    • @hasen 意思相似,但方式非常不同。
    • 另一个名称的 if 语句。
    • 不是语言陈述,而是方法。不同之处在于您只能将 if 用于返回布尔值的表达式;这也意味着如果SmallTalk 没有实现isA(class) 这样的方法,那么您就不能使用ifTrue: 来验证对象的类,这是OO 语言应该避免的。当然,并不是所有的语言都是纯面向对象的,它们实现的特性应该会使语言更强大(尽管这并不意味着这些特性会使语言更安全)。
    • @Andreas 你的想法太小了。 smalltalk 的关键在于它是所有对象(字符和整数、类、方法、浏览器、IDE 本身),这使得 smalltalk 在调试、重构、TDD 方面非常强大。实际上比任何最近的语言都更强大。
    【解决方案7】:

    这取决于语言。

    静态类型语言应该能够通过共享通用接口和重载函数/方法来处理所有类型检查。

    动态类型语言可能需要以不同的方式解决问题,因为在传递消息时不检查类型,仅在访问对象时(或多或少)。使用通用接口仍然是一种很好的做法,并且可以消除许多类型检查 if 语句。

    虽然某些构造通常是代码异味的标志,但我很犹豫是否要消除先验问题的任何方法。有时通过 if 进行类型检查是权宜之计。

    注意:其他人建议使用 switch 代替,但这只是编写更清晰的 if 语句的聪明方法。

    【讨论】:

      【解决方案8】:

      我过去经常像recommend in the anti-if campaign 那样编写代码,使用委托字典中的回调或多态性。

      这是一个相当令人着迷的论点,特别是如果您正在处理杂乱的代码库,但老实说,虽然它对于插件模型或简化大型嵌套 if 语句非常有用,但它确实使导航和可读性有点痛苦。

      例如,Visual Studio 中的 F12(转到定义)将带您到一个抽象类(或者,在我的例子中是一个接口定义)。

      它还使得对类的快速可视化扫描非常麻烦,并增加了设置委托和查找哈希的开销。

      在我看来,尽可能多地使用 anti-if 活动中提出的建议,看起来就像是“哦,新闪亮的东西”编程。

      至于本线程中提出的其他构造,尽管它是本着有趣的挑战的精神完成的,只是 if 语句的替代品,并没有真正解决反 if 的基本信念广告系列。

      【讨论】:

        【解决方案9】:

        是的。 if 语句暗示在许多现代处理器(尤其是 PowerPC)上可能非常昂贵的分支。许多现代 PC 会进行大量的流水线重新排序,因此分支错误预测可能会导致每个分支未命中超过 30 个周期。
        在控制台编程中,有时只执行代码并忽略它比检查是否应该执行它更快!

        C 中的简单分支避免:

        if (++i >= 15)
        {
            i = 0;
        )
        

        可以改写为

         i = (i + 1) & 15;  
        

        但是,如果你想看到一些真正的反if fu,那么请阅读this

        哦,关于 OOP 问题 - 我将用虚函数调用替换分支错误预测?不,谢谢....

        【讨论】:

        • 当然,如果您从 if 语句中进行优化,您可能会浪费优化时间,而这些时间本可以花在更复杂的结构上。
        • @Tchavalak : 取决于.. PS3 上的命中率非常高,所以时间花得值,但在另一个架构上它可能不是那么高的收益。一切都取决于硬件,它是真理的最终仲裁者!
        • 哦,缓存友好的代码是一个更好的时间投资。尤其是内存速度通常是大多数现代高性能应用程序(甚至大多数应用程序)的瓶颈
        • 这就是我喜欢安腾架构的地方。这些简短的 if 语句不需要进行分支预测。
        【解决方案10】:

        您可以使用对象定义 True 和 False(在伪 Python 中):

        class True:
            def if(then,else):
                return then
            def or(a):
                return True()
            def and(a):
                return a
            def not():
                return False()
        
        class False:
            def if(then,else):
                return false
            def or(a):
                return a
            def and(a):
                return False()
            def not():
                return True()
        

        我认为这是构造布尔值的一种优雅方式,它证明您可以用多态性替换每个 if,但这不是反 if 运动的重点。目标是避免编写诸如(在寻路算法中)之类的东西:

        if type == Block or type == Player:
            # You can't pass through this
        else:
            # You can
        

        而是在每个对象上调用一个 is_traversable 方法。从某种意义上说,这正是模式匹配的反面。 "if" 很有用,但在某些情况下,它并不是最好的解决方案。

        【讨论】:

        • 你能在一段简单的代码中举一个使用ifs和不使用ifs(使用你的类)的例子吗?
        • @ychaouche 在野外寻找specification pattern 的实现以获取真实示例。规范模式是此答案中模式的概括,其中不仅有两个条件,而且有任意数量的条件(每个都由一个类表示)。
        【解决方案11】:

        我假设您实际上是在询问替换检查类型的 if 语句,而不是替换所有 if 语句。

        用多态性替换 if 需要一个通用超类型中的方法,您可以通过直接覆盖它或通过重用访问者模式中的覆盖方法来进行调度。

        但是如果没有这样的方法,并且由于超类型不是由您维护,您无法将其添加到公共超类型中怎么办?你真的会不遗余力地引入一个新的超类型和子类型,只是为了摆脱一个 if 吗?在我看来,那将是纯洁性有点远。

        此外,这两种方法(直接覆盖和访问者模式)都有其缺点:直接覆盖方法要求您在要打开的类中实现您的方法,这可能无助于凝聚力。另一方面,如果多个案例共享相同的代码,访问者模式就很尴尬。如果可以的话:

        if (o instanceof OneType || o instanceof AnotherType) {
            // complicated logic goes here
        }
        

        您将如何与访问者模式共享代码?调用常用方法?你会把那个方法放在哪里?

        所以不,我不认为替换这样的 if 语句总是一种改进。通常是,但并非总是如此。

        【讨论】:

        • 我不同意您回答的精神,但共享逻辑示例可能不是最好的(至少如此处所述,以此类通用术语)。在这种情况下,很明显应该在基本类型和OneType/AnotherType 之间的层次结构中添加一个新类型。如果无法做到这一点,它通常表明可以进一步改进设计(通常是因为违反 SRP)。更多地思考功能会大有帮助。
        【解决方案12】:

        好吧,如果您使用 Perl 编写,这很容易!

        而不是

        if (x) {
            # ...
        }
        

        你可以使用

        unless (!x){
            # ... 
        }
        

        ;-)

        【讨论】:

          【解决方案13】:

          你可以在没有if 的情况下做到这一点,但如果没有允许你根据某些条件做出决定的机制,你就无法做到这一点。

          在汇编中,没有if 语句。有条件跳转。

          例如,在 Haskell 中,没有明确的 if,而是多次定义一个函数,我忘记了确切的语法,但它是这样的:

          伪haskell:

          def posNeg(x < 0):
              return "negative"
          
          def posNeg(x == 0):    
              return "zero"
          
          def posNeg(x):
              return "positive"
          

          调用posNeg(a)时,解释器会查看a的值,如果是&lt; 0则选择第一个定义,如果是== 0则选择第二个定义,否则将默认为第三个定义。

          因此,虽然 Haskell 和 SmallTalk 等语言没有通常的 C 风格 if 语句,但它们有其他方式让您做出决定。

          【讨论】:

          • 您正在寻找的确切语法是模式匹配,(我认为)反过来是 case 表达式的语法糖。
          • 如果有人想修复语法以匹配haskell,请随时编辑和修复(当然,如果你有足够的分数)
          【解决方案14】:

          这实际上是一个我喜欢用编程语言玩的编码游戏。它被称为“如果我们没有如果”,起源于:http://wiki.tcl.tk/4821

          基本上,如果我们不允许在语言中使用条件构造:没有 if、没有 while、没有 for、没有除非、没有 switch 等。我们可以重新创建自己的 IF 函数。答案取决于语言以及我们可以利用哪些语言特性(请记住,使用常规条件结构是在欺骗三元运算符!)

          例如,在 tcl 中,函数名只是一个字符串,任何字符串(包括空字符串)都可以用于任何内容(函数名、变量名等)。因此,我们可以利用这一点:

          proc 0 {true false} {uplevel 1 $false; # execute false code block, ignore true}
          proc 1 {true false} {uplevel 1 $true;  # execute true code block, ignore flase}
          
          proc _IF {boolean true false} {
              $boolean $true $false
          }
          
          #usage:
          _IF [expr {1<2}] {
              puts "this is true"
          } {
            #else:
              puts "this is false"
          }
          

          或者在 javascript 中,我们可以滥用松散的类型以及几乎任何东西都可以转换为字符串并将其与其功能性质相结合的事实:

          function fail (discard,execute) {execute()}
          function pass (execute,discard) {execute()}
          var truth_table = {
              'false' : fail,
              'true' : pass
          }
          function _IF (expr) {
            return truth_table[!!expr];
          }
          
          //usage:
          _IF(3==2)(
              function(){alert('this is true')},
          //else
              function(){alert('this is false')}
          );
          

          并非所有语言都可以做到这一点。但我喜欢的语言往往能够做到。

          【讨论】:

          • 曾经与我共事的人曾说过“在真正的 OO 中,你不需要 if”,但他的代码看起来完全相反。我想出了一个类似的解决方案。
          【解决方案15】:

          如果您将 if 保留在您的构造代码(工厂、构建器、提供者等)中,您可以避免在业务逻辑代码中使用 if。您的业​​务逻辑代码将更具可读性、更易于理解或更易于维护或扩展。见:http://www.youtube.com/watch?v=4F72VULWFvc

          【讨论】:

            【解决方案16】:

            在回答问题时,正如最后一位受访者所建议的那样,您需要一些 if 语句来检测工厂中的状态。然后,您将实例化一组协作类来解决特定于状态的问题。当然,根据需要需要其他条件,但它们会被最小化。

            当然要删除的是在如此多的基于服务的代码中普遍存在的无休止的过程状态检查。

            提到了有趣的 smalltalk,因为这是我在被拖入 Java 之前使用的语言。我不像以前那么早回家了。

            【讨论】:

              【解决方案17】:

              “anti-if”运动背后的原因与 Kent Beck 所说的类似:

              好的代码总是有小的方法和 小物件。只需将系统分解为许多小状态 和函数你是否希望满足“一次且仅一次”的规则。我得到很多 反对这个想法,特别是来自经验丰富的开发人员,但没有人 我对系统所做的事情与将系统分解成更多部分一样提供了帮助。

              如果您不知道如何通过组合和继承来分解程序,那么您的类和方法会随着时间的推移而变得越来越大。当您需要进行更改时,最简单的方法是在某处添加一个 IF。添加过多的 IF,您的程序将变得越来越难以维护,而最简单的方法仍然是添加更多的 IF。

              您没有必须将每个 IF 都变成对象协作;但是当你知道如何做时,这是一件非常好的事情:-)

              【讨论】:

              • 你用 搞定了它“但是当你知道如何做时这是一件非常好的事情”。有人出于实用目的而拒绝这个想法是一回事,但出于无知而这样做是另一回事。
              【解决方案18】:

              我考虑过添加我的两分钱:您可以优化许多语言中的 ifs,其中布尔表达式的第二部分在不会影响结果时不会被评估。

              使用and 运算符,如果第一个操作数的计算结果为false,则无需计算第二个操作数。使用or 运算符,情况正好相反——如果第一个操作数是true,则不需要计算第二个操作数。有些语言总是这样,有些则提供了另一种语法。

              这是用 JavaScript 编写的 if - elseif - else 代码,仅使用运算符和匿名函数。

              document.getElementById("myinput").addEventListener("change", function(e) {
              
                (e.target.value == 1 && !function() {
                  alert('if 1');
                }()) || (e.target.value == 2 && !function() {
                  alert('else if 2');
                }()) || (e.target.value == 3 && !function() {
                  alert('else if 3');
                }()) || (function() {
                  alert('else');
                }());
              
              });
              &lt;input type="text" id="myinput" /&gt;

              这让我想尝试定义一种深奥的语言,其中块隐含地表现得像自执行匿名函数并返回 true,这样你就可以这样写:

              (condition && {
                action
              }) || (condition && {
                action
              }) || {
                action
              }
              

              【讨论】:

              • 虽然它可能很有趣,但请注意,这个问题是关于多态性的,所以这并没有严格回答。
              猜你喜欢
              • 2013-02-22
              • 2016-08-21
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-02-06
              • 2010-09-13
              • 2015-07-24
              • 2021-05-17
              相关资源
              最近更新 更多