Java有懒惰的评估吗?

m0s*_*it0 27 java lazy-evaluation

我知道Java在这种情况下有智能/懒惰的评估:

public boolean isTrue() {
    boolean a = false;
    boolean b = true;
    return b || (a && b); // (a && b) is not evaluated since b is true
}
Run Code Online (Sandbox Code Playgroud)

但是关于:

public boolean isTrue() {
    boolean a = isATrue();
    boolean b = isBTrue();
    return b || a;
}
Run Code Online (Sandbox Code Playgroud)

isATrue()即使isBTrue()返回true 也会被调用?

eme*_*esx 28

那么,就语言而言 - 是的,这两个函数都被调用了.

如果你重写了这个函数:

public boolean isTrue() {
    return isBTrue() || isATrue();
}
Run Code Online (Sandbox Code Playgroud)

如果第一个函数为真,则不会调用第二个函数.


但这是短路评估,而不是懒惰的评估.懒惰的评估案例看起来像这样:

public interface LazyBoolean {
    boolean eval();
}

class CostlyComparison implements LazyBoolean {
  private int a, b;

  public CostlyComparison(int a, int b) { 
    this.a=a; 
    this.b=b; 
  }

  @Override 
  public boolean eval() {
    //lots of probably not-always-necessary computation here
    return a > b;
  }
} 

public LazyBoolean isATrue() {
  return new CostlyComparison(10,30);  //just an example
}

public boolean isTrue() {        // so now we only pay for creation of 2 objects
    LazyBoolean a = isATrue();   // but the computation is not performed; 
    LazyBoolean b = isBTrue();   // instead, it's encapsulated in a LazyBoolean
    return b.eval() || a.eval(); // and will be evaluated on demand;
                                 // this is the definition of lazy eval.
}
Run Code Online (Sandbox Code Playgroud)

  • *"但这是短路评估,而不是懒惰评估."*唔不,实际上是:*"短路评估只是(非常有限的情况下)懒惰评估"*. (5认同)

Oli*_*rth 21

在Java(和其他类C语言)中,这被称为短路评估.*

是的,在第二个例子isATrue中总是被调用.也就是说,除非编译器/ JVM可以确定它没有可观察到的副作用,在这种情况下它可能会选择优化,但在这种情况下你不会注意到差异.


*两者截然不同; 前者本质上是一种优化技术,而第二种是由语言强制执行,可以影响可观察的程序行为.

我最初建议这与懒惰评估完全不同,但正如@Ingo在下面的评论中指出的那样,这是一个可疑的断言.可以将Java中的短路运算符视为惰性求值的非常有限的应用.

但是,当函数式语言强制执行惰性评估语义时,通常会出于完全不同的原因,即防止无限(或至少是过度)递归.

  • 我没有投反对票,但你应得的。因为短路评估只是*是*(非常有限的)懒惰评估。 (2认同)

Ton*_*ris 17

不,Java只对用户定义的方法进行了热切的评估.正如您所注意到的,Java的一些语言结构实现了非严格的评估.其他的还包括if,?:,while.

我曾经学过[1],对于"懒惰的评价"意味着什么有些混乱.首先,惰性评估意味着按需调用评估.Java完全没有这样的东西.但是,有一个共同的趋势(我个人不鼓励)放松懒惰评估的定义,也包括逐个名称的评估.&&在按需调用和按名称调用评估时无法区分的功能; 无论如何都会是一样的,这会掩盖这个问题.

考虑到这种松动,一些人反驳说,Java声称对以下内容进行了懒惰评估:

interface Thunk<A> {
  A value();
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以编写用户定义,&&如下所示:

boolean and(boolean p, Thunk<Boolean> q) {
  return p && q();
}
Run Code Online (Sandbox Code Playgroud)

然后提出声明,这表明Java具有惰性评估.然而,即使在宽松的意义上,这也不是懒惰的评价.这里的区别在于Java的类型系统不统一类型boolean/ BooleanThunk<Boolean>.尝试将其中一个用作另一个将导致类型错误.在没有静态类型系统的情况下,代码仍然会失败.正是这种缺乏统一(静态打字与否)回答了这个问题; 不,Java没有用户定义的延迟评估.当然,它可以如上所述进行模拟,但这是一种无趣的观察,它源于图灵等价.

诸如Scala之类的语言具有逐个名称的评估,其允许用户定义的and函数,其等同于常规&&函数(考虑终止.参见[1]).

// note the => annotation on the second argument
def and(p: Boolean, q: => Boolean) =
  p && q
Run Code Online (Sandbox Code Playgroud)

这允许:

def z: Boolean = z
val r: Boolean = and(false, z)
Run Code Online (Sandbox Code Playgroud)

请注意,此短程序片段通过提供值终止.它还将类型的值统一Boolean为call-by-name.因此,如果您订阅了懒惰评估的松散定义(同样,我不鼓励这样做),您可能会说Scala有懒惰的评估.我在这里提供Scala作为一个很好的对比.我建议查看Haskell的真正懒惰评估(按需调用).

希望这可以帮助!

[1] http://blog.tmorris.net/posts/a-fling-with-lazy-evaluation/


小智 11

SE8(JDK1.8)引入了Lambda表达式,可以使惰性求值更加透明.考虑以下代码的main方法中的语句:

@FunctionalInterface
public interface Lazy<T> {
   T value();
}

class Test {
   private String veryLongMethod() {
      //Very long computation
      return "";
   }

   public static <T> T coalesce(T primary, Lazy<T> secondary) {
      return primary != null? primary : secondary.value();
   }

   public static void main(String[] argv) {
      String result = coalesce(argv[0], ()->veryLongMethod());
   }
}
Run Code Online (Sandbox Code Playgroud)

被调用函数coalesce返回第一个给定的非空值(如在SQL中).其调用中的第二个参数是Lambda表达式.仅当argv [0] == null时才会调用veryLongMethod()方法.在这种情况下,唯一的有效负载是()->在按需延迟评估的值之前插入.

  • 我会使用现有的java.util.function.Supplier作为功能接口,但是创建一个名为Lazy的功能接口可能更清楚地显示了意图. (2认同)