繁体   English   中英

Java有懒惰的评估吗?

[英]Does Java have 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
}

但是关于:

public boolean isTrue() {
    boolean a = isATrue();
    boolean b = isBTrue();
    return b || a;
}

isATrue()调用,即使isBTrue()返回true?

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

如果你重写了这个函数:

public boolean isTrue() {
    return isBTrue() || isATrue();
}

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


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

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.
}

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

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


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

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

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

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

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

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

interface Thunk<A> {
  A value();
}

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

boolean and(boolean p, Thunk<Boolean> q) {
  return p && q();
}

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

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

// note the => annotation on the second argument
def and(p: Boolean, q: => Boolean) =
  p && q

这允许:

def z: Boolean = z
val r: Boolean = and(false, z)

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

希望这可以帮助!

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

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());
   }
}

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

为简单起见,您可以使用java 8中的Supplier接口,如下所示:

Supplier<SomeVal> someValSupplier = () -> getSomeValLazily();

然后在代码之后你可以:

if (iAmLazy) 
  someVal = someValSupplier.get(); // lazy getting the value
else 
  someVal = getSomeVal(); // non lazy getting the value

只是想添加除了这个问题线程中提到的内容,下面是来自JVM上的Oracle Documentation

Java虚拟机实现可以选择在使用它时分别解析类或接口中的每个符号引用(“懒惰”或“迟”分辨率),或者在验证类时立即解析它们(“渴望”)或“静态”解决方案)。 这意味着在一些实现中,在初始化类或接口之后,解析过程可以继续。

参考

作为具有延迟实现的类的示例是Stream,这来自Oracle Documentation on Stream

溪流很懒; 仅在启动终端操作时执行对源数据的计算,并且仅在需要时消耗源元素。

参考

话虽如此,如果您执行以下操作,则不会显示任何内容。 除非您添加启动器。

Steam.of(1, 2, 3, 4, 5).filter(number -> {
   System.out.println("This is not going to be logged");
   return true;
});

如果isBTrue()返回true,则调用isATrue()吗?

是的,两者都被称为。

不,不是的。 isBTrue()将被调用,而不管结果, isATrue() 您可以通过编写这样的程序来自行验证,并在每个方法中使用print语句。

是的isATrue()将被调用,因为你在行boolean a = isATrue();显式调用它boolean a = isATrue();

但是如果isBTrue()返回true ,则不会在以下情况下调用它:

public boolean isTrue() {
    return isBTrue() || isATrue();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM