简体   繁体   English

Java有懒惰的评估吗?

[英]Does Java have lazy evaluation?

I know that Java has smart/lazy evaluation in this case: 我知道Java在这种情况下有智能/懒惰的评估:

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

But what about: 但是关于:

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

Is isATrue() called even if isBTrue() returns true? isATrue()调用,即使isBTrue()返回true?

Well, as far as the language is concerned - yes, both functions are called. 那么,就语言而言 - 是的,这两个函数都被调用了。

If you rewrote the function to this: 如果你重写了这个函数:

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

then the second function will not be called, if the first is true. 如果第一个函数为真,则不会调用第二个函数。


But this is short-circuit evaluation , not lazy evaluation . 但这是短路评估 ,而不是懒惰的评估 Lazy evaluation case would look something like this: 懒惰的评估案例看起来像这样:

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

In Java (and other C-like languages), this is referred to as short-circuit evaluation . 在Java(和其他类C语言)中,这被称为短路评估 * *

And yes, in the second example isATrue is always called. 是的,在第二个例子中,始终调用isATrue That is, unless the compiler/JVM can determine that it has no observable side-effects, in which case it may choose to optimize, but in which case you wouldn't notice the difference anyway. 也就是说,除非编译器/ JVM可以确定它没有可观察到的副作用,在这种情况下它可能会选择优化,但在这种情况下你不会注意到差异。


* The two are quite distinct; * 两者截然不同; the former is essentially an optimization technique, whereas the second is mandated by the language and can affect observable program behaviour. 前者本质上是一种优化技术,而第二种是由语言强制执行,可以影响可观察的程序行为。

I originally suggested that this was quite distinct from lazy evaluation, but as @Ingo points out in comments below, that's a dubious assertion. 我最初建议这与懒惰评估完全不同,但正如@Ingo在下面的评论中指出的那样,这是一个可疑的断言。 One may view the short-circuit operators in Java as a very limited application of lazy evaluation. 可以将Java中的短路运算符视为惰性求值的非常有限的应用。

However, when functional languages mandate lazy-evaluation semantics, it's usually for a quite different reason, namely prevention of infinite (or at least, excessive) recursion. 但是,当函数式语言强制执行惰性评估语义时,通常会出于完全不同的原因,即防止无限(或至少是过度)递归。

No, Java has only eager evaluation for user-defined methods. 不,Java只对用户定义的方法进行了热切的评估。 Some of Java's language constructs implement non-strict evaluation as you note. 正如您所注意到的,Java的一些语言结构实现了非严格的评估。 Others include if , ?: , while . 其他包括if?:while

I once learned[1] that there is some confusion around what it means to "have lazy evaluation." 我曾经学过[1],对于“懒惰的评价”意味着什么有些混乱。 First, lazy evaluation means call-by-need evaluation. 首先,惰性评估意味着按需调用评估。 Java has nothing at all like this. Java完全没有这样的东西。 However, there is a common trend (which I personally discourage) to loosen the definition of lazy evaluation to also include call-by-name evaluation. 但是,有一个共同的趋势(我个人不鼓励)放松懒惰评估的定义,也包括逐个名称的评估。 Functions such as && cannot be distinguished under call-by-need versus call-by-name evaluation; 诸如&&功能无法在按需调用与按名称调用评估之间进行区分; it would be the same regardless, which obscures the matter. 无论如何都会是一样的,这会掩盖这个问题。

Taking this loosening into account, some further counter-argue by claiming Java has lazy evaluation by the following: 考虑到这种松动,一些人反驳说,Java声称对以下内容进行了懒惰评估:

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

Then, you might write a user-defined && like so: 然后,您可以编写用户定义的&&如下所示:

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

The claim is then put forward that this demonstrates that Java has lazy evaluation. 然后提出声明,这表明Java具有惰性评估。 However, this is not lazy evaluation, even in the loose sense. 然而,即使在宽松的意义上,这也不是懒惰的评价。 The distinguishing point here is that Java's type system does not unify the types boolean / Boolean and Thunk<Boolean> . 这里的区别点是Java的类型系统没有统一类型boolean / BooleanThunk<Boolean> Attempting to use one as the other will result in a type-error . 尝试将其中一个用作另一个将导致类型错误 In the absence of a static type system, the code would still fail. 在没有静态类型系统的情况下,代码仍然会失败。 It is this lack of unification (static typing or not) that answers the question; 正是这种缺乏统一(静态打字与否)回答了这个问题; no, Java does not have user-defined lazy evaluation. 不,Java没有用户定义的延迟评估。 Of course, it can be emulated as above, but this is an uninteresting observation which follows from turing-equivalence. 当然,它可以如上所述进行模拟,但这是一种无趣的观察,它源于图灵等价。

A language such as Scala has call-by-name evaluation, which allows a user-defined and function which is equivalent to the regular && function (taking termination into account. See [1]). 诸如Scala之类的语言具有按名称调用评估,其允许用户定义and功能,其等同于常规&&功能(考虑终止。参见[1])。

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

This allows: 这允许:

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

Note that this short program snippet terminates by providing a value. 请注意,此短程序片段通过提供值终止 It also unifies values of type Boolean as call-by-name. 它还将Boolean类型的Boolean 统一为call-by-name。 Therefore, if you subscribe to the loose definition of lazy evaluation (and again, I discourage this), you might say that Scala has lazy evaluation. 因此,如果您订阅了懒惰评估的松散定义(同样,我不鼓励这样做),您可能会说Scala有懒惰的评估。 I provide Scala here as a good contrast. 我在这里提供Scala作为一个很好的对比。 I recommend looking at Haskell for true lazy evaluation (call-by-need). 我建议查看Haskell的真正懒惰评估(按需调用)。

Hope this helps! 希望这可以帮助!

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

SE8 (JDK1.8) has introduced Lambda expressions , which can make lazy evaluations more transparent. SE8(JDK1.8)引入了Lambda表达式 ,可以使惰性求值更加透明。 Consider the statement in the main method of the following code: 考虑以下代码的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());
   }
}

The called function coalesce returns the first given non-null value (as in SQL). 被调用函数coalesce返回第一个给定的非空值(如在SQL中)。 The second parameter in its call is a Lambda expression. 其调用中的第二个参数是Lambda表达式。 The method veryLongMethod() will be called only when argv[0] == null. 仅当argv [0] == null时才会调用veryLongMethod()方法。 The only payload in this case is inserting ()-> before the value to be lazily evaluated on demand. 在这种情况下唯一的有效负载是在按需延迟评估的值之前插入()->

For simplicity you can use the Supplier interface from java 8 like this: 为简单起见,您可以使用java 8中的Supplier接口,如下所示:

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

Then in code afterwards you can have: 然后在代码之后你可以:

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

Just wanted to add in addition to what have been mentioned in this question thread, below is from Oracle Documentation on JVM 只是想添加除了这个问题线程中提到的内容,下面是来自JVM上的Oracle Documentation

a Java Virtual Machine implementation may choose to resolve each symbolic reference in a class or interface individually when it is used ("lazy" or "late" resolution), or to resolve them all at once when the class is being verified ("eager" or "static" resolution). Java虚拟机实现可以选择在使用它时分别解析类或接口中的每个符号引用(“懒惰”或“迟”分辨率),或者在验证类时立即解析它们(“渴望”)或“静态”解决方案)。 This means that the resolution process may continue, in some implementations, after a class or interface has been initialized. 这意味着在一些实现中,在初始化类或接口之后,解析过程可以继续。

reference 参考

and as an example of the classes that has lazy implementation is Stream, this is from Oracle Documentation on Stream 作为具有延迟实现的类的示例是Stream,这来自Oracle Documentation on Stream

Streams are lazy; 溪流很懒; computation on the source data is only performed when the terminal operation is initiated, and source elements are consumed only as needed. 仅在启动终端操作时执行对源数据的计算,并且仅在需要时消耗源元素。

reference 参考

That being said, if you do the following, nothing will be displayed. 话虽如此,如果您执行以下操作,则不会显示任何内容。 Unless you add initiator. 除非您添加启动器。

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

Is isATrue() called if isBTrue() returns true? 如果isBTrue()返回true,则调用isATrue()吗?

Yes, both are called. 是的,两者都被称为。

No, it does not. 不,不是的。 isBTrue() will be invoked, regardless of the result of isATrue() . isBTrue()将被调用,而不管结果, isATrue() You can verify this yourself by writing such a program, with print statements in each of the methods. 您可以通过编写这样的程序来自行验证,并在每个方法中使用print语句。

Yes isATrue() will be called because you are calling it explicitly in line boolean a = isATrue(); 是的isATrue()将被调用,因为你在行boolean a = isATrue();显式调用它boolean a = isATrue();

But it won't be called in following case if isBTrue() returns true : 但是如果isBTrue()返回true ,则不会在以下情况下调用它:

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

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

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