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

Звонить по вызову по вызову по имени

Я не понимал разницы между Call-by-name и Call-by-need. Как я понял, метод "по требованию" возвращает возвращаемый ответ. Но как это помогает нам, и есть ли принципиальная разница между результатами?

Например,

begin integer n;
  procedure foo(e, n);
  integer e, n;
  begin
    for n := 1 step 1 until 10 do begin
      prints(`;;; the value of e is ');
      printnln(e)
    end
  end;
  foo(2 * n, n)
end

Таким образом, во имя вызова, как я понял, мы получим:

;;; the value of e is 2
;;; the value of e is 4
;;; the value of e is 8

и т.д. Это происходит потому, что мы передаем 2*n в e, а e оценивается с новым i каждый раз. Что произойдет в случае необходимости?

4b9b3361

Ответ 1

В вызове по необходимости мы вводим цикл и оцениваем значение только один раз. Итак, в приведенном выше коде мы скопируем (2*n) внутри цикла (макро-стиль), и мы будем оценивать выражение только один раз (не как вызов по имени). Итак, на первой итерации мы получим e=2. это будет значение e также в следующей итерации, а выход будет:

;;; the value of e is 2
;;; the value of e is 2
;;; the value of e is 2

Ответ 2

Кажется, что ваше замешательство проистекает из того факта, что вы думаете в контексте imperative. Дискуссия по требованию по требованию по отношению к колл-значению в основном связана с декларативными и функциональными языками и лямбда-исчислением.

В этой статье вы можете узнать о стратегиях оценки, что как позывные, так и по требованию считаются ленивыми стратегиями оценки. Ленивая оценка означает, что когда выражение передается как параметр функции, оно не оценивается перед входом в тело функции, но только при первом обращении к нему/чтении в функции. Если результат такого выражения никогда не используется внутри, он никогда не будет оценен.

Например, оператор ? : ленив на Java, как показано ниже:

String test(Object obj)
{
    return 1 == 2 ? obj.toString() : "Hello World";
}

test(null); // this won't throw a NullPointerException

Call-by-need является важной особенностью большинства функциональных языков, которые имеют чистое подмножество. В чисто функциональном языке каждая функция должна быть ссылочно прозрачной, то есть они не могут побочные эффекты. Такие чистые функции обладают тем свойством, что для некоторого заданного ввода они всегда возвращают один и тот же результат, независимо от того, сколько раз вызывали, и что они никогда ничего не меняют в "состоянии мира". Они ведут себя точно так же, как математические функции, написанные на бумаге.

Как вы уже поняли, стратегия call-by-need невозможна при вызове нечистых функций, потому что вас больше всего интересуют побочные эффекты из-за последовательных вызовов. С другой стороны, это становится важной характеристикой производительности при использовании в чисто функциональных языках (см. Второй пример ниже). Также см. Эти страницы wiki о концепциях Сокращение графика и Memoization.

Примеры в реальном мире

Первый. Один из примеров широко используемой системы, использующей сокращение графика, - Apache Ant. Ant не оценивает цель дважды. Эта конструкция упрощает наброски декларативного плана сборки.

Второй.. Если вы хотите увидеть хорошую демонстрацию memoization, введите этот Haskell код в интерпретатор GHC и посмотреть, что произойдет:

Prelude> let fibs = 0:1:(zipWith (+) fibs (tail fibs))
-- This defines the Fibonacci sequence.
Prelude> fibs !! 200000
-- Prints the 200,000th Fibonacci number,
-- takes several seconds to calculate.
Prelude> fibs !! 200000
-- Prints the same number as before,
-- but this time it returns immediately.

Примечание. Возможно, вы также слышали о стратегии оценки по умолчанию. В отличие от звонящего по имени и по требованию, call-by-value является строгой стратегией оценки. Это похоже на вызов по имени в том смысле, что вызов нескольких раз приводит к множественной оценке. Это наиболее распространенная парадигма для программистов, привыкших к императивным языкам, таким как С# или Java.

Ответ 3

Call-by-name - это функция, вызывающая дисциплину, когда при вызове функции приема foo вместо аргументов foo, которые вы оцениваете, foo получает (за кулисами) соответствующий объект, который позволит ему оценивать требуемые параметры; или, что эквивалентно, оценка выполняется путем макроподстановки. Если параметр требуется более одного раза, он будет оцениваться несколько раз. См.: http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_name

Вызов по очереди - это то же самое, за исключением того, что переданный объект является обещанием и будет оцениваться не более одного раза; при последующих ссылках на параметр используется memoised value. См.: http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_need

Ответ 4

Прежде всего, вызов по запросу или вызов по имени представляют собой способы реализации ленивой оценки, поэтому необходимо знать, что такое lazy -оценка...

В Haskell вы можете увидеть вызов по запросу и, например, вызов по имени в Scala.

call-by-need - это, возможно, ожидаемый способ реализации лени. В первый раз, когда вам "действительно" нужно значение, вы его вычислили, и вы создаете кеш-память вычисленного значения для будущего доступа.

когда у вас есть call-by-name, вы не выполняете такого рода кеширование, вы вычисляете аргументы функции каждый раз, когда они используются в теле функции. Можно подумать, что это не имеет смысла, но полезно реализовать некоторую структуру управления потоком как функцию языка, скажем немного.

def myWhile (cond : => Boolean, body : => Unit) {
  if (cond) { body ; myWhile (cond, body) }
}

И тогда вы можете вызвать функцию myWhile,

var x = 3

myWhile (x != 0, {
  print (x)
  x = x - 1   
})

Если бы мы использовали call-by-name для приведенного выше примера, выражение "cond" не кэшируется и каждый раз его оценивают.