Подтвердить что ты не робот

Является ли Java ленивой оценкой?

Я знаю, что 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?

4b9b3361

Ответ 1

В Java (и других C-подобных языках) это называется оценка короткого замыкания. *

И да, во втором примере всегда вызывается isATrue. То есть, если компилятор /JVM не может определить, что он не имеет наблюдаемых побочных эффектов, и в этом случае он может выбрать оптимизацию, но в этом случае вы все равно не заметили бы разницу.


* Эти два совершенно различны; первый по сути является методом оптимизации, тогда как второй задается языком и может влиять на наблюдаемое поведение программы.

Я изначально предположил, что это было совершенно отличным от ленивой оценки, но, как @Ingo указывает в комментариях ниже, это сомнительное утверждение. Можно рассматривать операторов короткого замыкания на Java как очень ограниченное применение ленивой оценки.

Однако, когда функциональные языки выполняют семантику ленивой оценки, это обычно по совершенно другой причине, а именно по предотвращению бесконечной (или, по крайней мере, чрезмерной) рекурсии. Суб >

Ответ 2

Хорошо, что касается языка - да, обе функции вызываются.

Если вы переписали эту функцию:

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 encapsulated in a LazyBoolean
    return b.eval() || a.eval(); // and will be evaluated on demand;
                                 // this is the definition of lazy eval.
}

Ответ 3

Нет, 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/boolean и Thunk<Boolean>. Попытка использовать его как другой приведет к ошибке типа. В отсутствие системы статического типа код все равно будет терпеть неудачу. Именно этот недостаток унификации (статическая типизация или нет) отвечает на вопрос; нет, у Java нет пользовательской ленивой оценки. Конечно, его можно эмулировать, как указано выше, но это неинтересное наблюдение, которое следует из turing-эквивалентности.

Язык, такой как 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 в качестве вызова по имени. Поэтому, если вы подписаны на свободное определение ленивой оценки (и опять же, я отговариваю это), вы можете сказать, что Scala имеет ленивую оценку. Я предлагаю Scala здесь как хороший контраст. Я рекомендую посмотреть на Haskell для истинной ленивой оценки (по требованию).

Надеюсь, это поможет!

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

Ответ 4

SE8 (JDK1.8) ввел выражения лямбда, которые могут сделать ленивые оценки более прозрачными. Рассмотрим инструкцию в основном методе следующего кода:

@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). Второй параметр в его вызове - это выражение Лямбды. Метод veryLongMethod() будет вызываться только тогда, когда argv [0] == null. Единственной полезной нагрузкой в ​​этом случае является вставка ()-> до того, как значение будет оцениваться по ленивому запросу.

Ответ 5

Для простоты вы можете использовать интерфейс поставщика из java 8 следующим образом:

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

Затем в коде впоследствии вы можете:

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

Ответ 6

Вызывается isATrue(), если isBTrue() возвращает true?

Да, оба вызываются.

Ответ 7

Нет, это не так. isBTrue() будет вызываться независимо от результата isATrue(). Вы можете проверить это самостоятельно, написав такую ​​программу с помощью операторов печати в каждом из методов.

Ответ 8

Да isATrue() вызывается, потому что вы вызываете его явно в строке boolean a = isATrue();

Но он не будет вызываться в следующем случае, если isBTrue() возвращает true:

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