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)
Oli*_*rth 21
在Java(和其他类C语言)中,这被称为短路评估.*
是的,在第二个例子isATrue中总是被调用.也就是说,除非编译器/ JVM可以确定它没有可观察到的副作用,在这种情况下它可能会选择优化,但在这种情况下你不会注意到差异.
我最初建议这与懒惰评估完全不同,但正如@Ingo在下面的评论中指出的那样,这是一个可疑的断言.可以将Java中的短路运算符视为惰性求值的非常有限的应用.
但是,当函数式语言强制执行惰性评估语义时,通常会出于完全不同的原因,即防止无限(或至少是过度)递归.
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/ Boolean和Thunk<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()方法.在这种情况下,唯一的有效负载是()->在按需延迟评估的值之前插入.