В приведенном вами примере используется только вызов по значению, поэтому я приведу новый, более простой пример, показывающий разницу.
Во-первых, давайте предположим, что у нас есть функция с побочным эффектом. Эта функция печатает что-то, а затем возвращает Int
.
def something() = {
println("calling something")
1 // return value
}
Теперь мы собираемся определить две функции, которые принимают Int
одинаковые аргументы, за исключением того, что одна принимает аргумент в стиле call-by-value ( x: Int
), а другая - в стиле call-by-name ( x: => Int
).
def callByValue(x: Int) = {
println("x1=" + x)
println("x2=" + x)
}
def callByName(x: => Int) = {
println("x1=" + x)
println("x2=" + x)
}
Что происходит, когда мы вызываем их с нашей побочной функцией?
scala> callByValue(something())
calling something
x1=1
x2=1
scala> callByName(something())
calling something
x1=1
calling something
x2=1
Таким образом, вы можете видеть, что в версии с вызовом по значению побочный эффект переданного вызова функции ( something()
) произошел только один раз. Тем не менее, в версии по имени, побочный эффект произошел дважды.
Это связано с тем, что функции вызова по значению вычисляют значение переданного выражения перед вызовом функции, поэтому каждый раз к одному и тому же значению обращаются. Вместо этого функции вызова по имени повторно вычисляют значение переданного выражения при каждом обращении к нему.
=> Int
отличается от типаInt
; это «функция без аргументов, которая будет генерироватьInt
» против простоInt
. Получив первоклассные функции, вам не нужно придумывать терминологию для вызова по имени, чтобы описать это.f(2)
скомпилирован как выражение типаInt
, сгенерированный код вызываетf
аргумент,2
и результатом является значение выражения. Если этот же текст скомпилирован как выражение типа,=> Int
то сгенерированный код использует ссылку на некий «кодовый блок» в качестве значения выражения. В любом случае значение этого типа может быть передано функции, ожидающей параметр этого типа. Я почти уверен, что вы можете сделать это с помощью присваивания переменных без передачи параметров в поле зрения. Так при чем тут имена или звонки?=> Int
это «функция без аргументов, которая генерирует Int», как это отличается от() => Int
? Кажется, что Scala относится к ним по-разному, например,=> Int
очевидно, не работает как тип aval
, только как тип параметра.=> Int
это удобство, и оно не реализовано в точности как функциональный объект (вероятно, почему у вас не может быть переменных типа=> Int
, хотя нет фундаментальной причины, по которой это не могло бы работать).() => Int
это явно зависит от каких - либо аргументов , которые будут возвращатьInt
, которая должна быть вызвана явно и может быть передан в качестве функции.=> Int
это своего рода «проксиInt
», и единственное, что вы можете с ним сделать, это вызвать его (неявно), чтобы получитьInt
.Вот пример от Мартина Одерского:
Мы хотим изучить стратегию оценки и определить, какая из них быстрее (меньше шагов) в следующих условиях:
вызов по значению: тест (2,3) -> 2 * 2 -> 4
вызов по имени: тест (2,3) -> 2 * 2 -> 4
Здесь результат достигается с таким же количеством шагов.
вызов по значению: тест (7,8) -> 7 * 7 -> 49
вызов по имени: (3 + 4) (3 + 4) -> 7 (3 + 4) -> 7 * 7 -> 49
Здесь вызов по значению быстрее.
вызов по значению: тест (7,8) -> 7 * 7 -> 49
вызов по имени: 7 * 7 -> 49
Здесь вызов по имени быстрее
вызов по значению: тест (7,2 * 4) -> тест (7, 8) -> 7 * 7 -> 49
вызов по имени: (3 + 4) (3 + 4) -> 7 (3 + 4) -> 7 * 7 -> 49
Результат достигается за те же шаги.
источник
def test (x:Int, y: => Int) = x * x
что параметр y никогда не используется.В вашем примере все параметры будут оценены до того, как она будет вызвана в функции, так как вы определяете их только по значению . Если вы хотите определить свои параметры по имени, вы должны передать блок кода:
Таким образом, параметр
x
не будет оцениваться до тех пор, пока он не будет вызван в функции.Этот небольшой пост здесь также объясняет это хорошо.
источник
Чтобы повторить точку зрения @ Бена в вышеприведенных комментариях, я думаю, что лучше всего рассматривать «вызов по имени» как просто синтаксический сахар. Парсер просто оборачивает выражения в анонимные функции, чтобы их можно было вызывать на более позднем этапе, когда они используются.
По сути, вместо определения
и работает:
Вы также можете написать:
И запустите его следующим образом для того же эффекта:
источник
=> T
и() => T
. Функция, которая принимает первый тип в качестве параметра, не примет второй, scala хранит достаточно информации в@ScalaSignature
аннотации, чтобы вызвать ошибку времени компиляции для этого. Байт-код для обоих=> T
и() => T
один и тот же, хотя и являетсяFunction0
. Смотрите этот вопрос для более подробной информации.Я попытаюсь объяснить простым вариантом использования, а не просто предоставив пример
Представьте, что вы хотите создать «приложение для наггеров» которое будет вас каждый раз с тех пор, как вы в последний раз были ворчливыми.
Изучите следующие реализации:
В вышеприведенной реализации наггер будет работать только при передаче по имени, причина в том, что при передаче по значению оно будет использоваться повторно, и поэтому значение не будет переоцениваться, в то время как при передаче по имени значение будет переоцениваться каждые время доступа к переменным
источник
Как правило, параметры функций являются побочными параметрами; значение параметра определяется до его передачи в функцию. Но что если нам нужно написать функцию, которая принимает в качестве параметра выражение, которое мы не хотим оценивать, пока оно не будет вызвано внутри нашей функции? Для этого обстоятельства Scala предлагает параметры по имени.
Механизм вызова по имени передает кодовый блок вызываемому абоненту, и каждый раз, когда вызываемый абонент обращается к параметру, выполняется кодовый блок и вычисляется значение.
источник
Как я предполагаю,
call-by-value
функция, как описано выше, передает только значения функции. Согласно «Martin Odersky
Это стратегия оценки», за которой следует Scala, которая играет важную роль в оценке функций. Но, сделать это простоcall-by-name
. Это как передача функции в качестве аргумента метода, также известного какHigher-Order-Functions
. Когда метод получает доступ к значению переданного параметра, он вызывает реализацию переданных функций. как ниже:В соответствии с примером @dhg, сначала создайте метод как:
Эта функция содержит один
println
оператор и возвращает целочисленное значение. Создайте функцию, у которой есть аргументы в видеcall-by-name
:Этот параметр функции определяет анонимную функцию, которая возвращает одно целочисленное значение. В нем
x
содержится определение функции, которая0
передала аргументы, но возвращаетint
значение, и нашаsomething
функция содержит одну и ту же сигнатуру. Когда мы вызываем функцию, мы передаем функцию в качестве аргументаcallByName
. Но в случаеcall-by-value
его только передать целочисленное значение функции. Мы вызываем функцию, как показано ниже:При этом наш
something
метод вызывается дважды, потому что, когда мы обращаемся к значению методаx
incallByName
, вызывается его определениеsomething
метода.источник
Вызов по значению - это общий случай использования, который объясняется многими ответами
Ниже я попытаюсь продемонстрировать более простой способ вызова по имени с примерами использования.
Пример 1:
Простой пример / пример использования вызова по имени находится ниже функции, которая принимает функцию в качестве параметра и дает истекшее время.
Пример 2:
apache spark (со scala) использует ведение журналов с использованием вызова по имени, чтобы увидеть
Logging
черту, в которой лениво оценивает, используется лиlog.isInfoEnabled
приведенный ниже метод.источник
При вызове по значению значение выражения предварительно вычисляется во время вызова функции, и это конкретное значение передается в качестве параметра соответствующей функции. Одно и то же значение будет использоваться во всей функции.
Принимая во внимание, что при вызове по имени само выражение передается функции в качестве параметра и вычисляется только внутри функции, когда вызывается этот конкретный параметр.
Разницу между Call by Name и Call by Value в Scala можно лучше понять с помощью приведенного ниже примера:
Фрагмент кода
Вывод
В приведенном выше фрагменте кода для вызова функции CallbyValue (System.nanoTime ()) системное время nano предварительно рассчитывается, и это предварительно вычисленное значение было передано параметром в вызов функции.
Но при вызове функции CallbyName (System.nanoTime ()) само выражение «System.nanoTime ())» передается как параметр вызова функции, и значение этого выражения вычисляется, когда этот параметр используется внутри функции. ,
Обратите внимание на определение функции функции CallbyName, где есть символ =>, разделяющий параметр x и его тип данных. Этот конкретный символ указывает на то, что функция вызывается по типу имени.
Другими словами, аргументы функции вызова по значению оцениваются один раз перед входом в функцию, но аргументы функции вызова по имени оцениваются внутри функции только тогда, когда они необходимы.
Надеюсь это поможет!
источник
Вот быстрый пример, который я кодировал, чтобы помочь моему коллеге, который в настоящее время проходит курс Scala. Что мне показалось интересным, так это то, что Мартин не использовал в качестве примера ответ на вопрос &&, представленный ранее в лекции. В любом случае, я надеюсь, что это поможет.
Вывод кода будет следующим:
источник
Параметры обычно передаются по значению, что означает, что они будут оцениваться перед заменой в теле функции.
Вы можете заставить параметр вызываться по имени, используя двойную стрелку при определении функции.
источник
Уже есть много фантастических ответов на этот вопрос в Интернете. Я напишу сборник нескольких объяснений и примеров, которые я собрал по этой теме, на случай, если кто-то может найти это полезным
ВВЕДЕНИЕ
вызов по значению (CBV)
Обычно параметры функций являются параметрами вызова по значению; то есть параметры оцениваются слева направо, чтобы определить их значение, прежде чем оценивается сама функция
вызов по имени (CBN)
Но что, если нам нужно написать функцию, которая принимает в качестве параметра выражение, которое мы не должны вычислять, пока оно не будет вызвано внутри нашей функции? Для этого обстоятельства Scala предлагает параметры по имени. Это означает, что параметр передается в функцию как есть, и его оценка происходит после подстановки.
Механизм вызова по имени передает кодовый блок на вызов, и каждый раз, когда вызов обращается к параметру, выполняется кодовый блок и вычисляется значение. В следующем примере, delayed печатает сообщение, показывающее, что метод был введен. Далее с задержкой печатается сообщение со своим значением. Наконец, отложенное возвращение 't':
Плюсы и минусы для каждого случая
CBN: + чаще завершается * проверка ниже, чем завершение выше * + Преимущество в том, что аргумент функции не оценивается, если соответствующий параметр не используется при оценке тела функции. - Это медленнее, он создает больше классов (то есть программа занимает загружается дольше) и потребляет больше памяти.
CBV: + Он часто экспоненциально более эффективен, чем CBN, потому что он избегает повторных вычислений аргументов, вызываемых по имени. Он оценивает каждый аргумент функции только один раз. Он играет намного лучше с императивными и побочными эффектами, потому что вы, как правило, лучше знаете, когда будут оцениваться выражения. -Это может привести к циклу при оценке его параметров * проверка ниже прекращения *
Что делать, если прекращение не гарантировано?
-Если CBV-оценка выражения e завершается, то CBN-оценка e также завершается. -Неное направление неверно
Пример не прекращения
Сначала рассмотрим выражение (1, цикл)
CBN: первый (1, цикл) → 1 CBV: первый (1, цикл) → уменьшить аргументы этого выражения. Так как каждый является циклом, он сокращает аргументы бесконечно. Не заканчивается
ОТЛИЧИЯ В ПОВЕДЕНИИ КАЖДОГО СЛУЧАЯ
Давайте определим метод теста, который будет
Тест Case1 (2,3)
Так как мы начинаем с уже оцененных аргументов, будет одинаковое количество шагов для вызова по значению и вызова по имени
Тест Case2 (3 + 4,8)
В этом случае вызов по значению выполняет меньше шагов
Тест Case3 (7, 2 * 4)
Мы избегаем ненужных вычислений второго аргумента
Тест Case4 (3 + 4, 2 * 4)
Другой подход
Во-первых, давайте предположим, что у нас есть функция с побочным эффектом. Эта функция печатает что-то, а затем возвращает Int.
Теперь мы собираемся определить две функции, которые принимают аргументы Int, которые в точности совпадают, за исключением того, что одна принимает аргумент в стиле вызова по значению (x: Int), а другая - в стиле вызова по имени (x: => Int).
Что происходит, когда мы вызываем их с нашей побочной функцией?
Таким образом, вы можете видеть, что в версии с вызовом по значению побочный эффект переданного вызова функции (нечто ()) произошел только один раз. Тем не менее, в версии по имени, побочный эффект произошел дважды.
Это связано с тем, что функции вызова по значению вычисляют значение переданного выражения перед вызовом функции, поэтому каждый раз к одному и тому же значению обращаются. Однако функции вызова по имени повторно вычисляют значение переданного выражения при каждом обращении к нему.
ПРИМЕРЫ ГДЕ ЛУЧШЕ ИСПОЛЬЗОВАТЬ CALL-BY-NAME
От: https://stackoverflow.com/a/19036068/1773841
Простой пример производительности: регистрация.
Давайте представим такой интерфейс:
А потом использовал вот так:
Если информационный метод ничего не делает (потому что, скажем, уровень ведения журнала был настроен на более высокий уровень), то computeTimeSpent никогда не вызывается, что экономит время. Это часто случается с регистраторами, где часто можно увидеть манипуляции со строками, которые могут быть дорогостоящими по сравнению с регистрируемыми задачами.
Пример корректности: логические операторы.
Вы, наверное, видели такой код:
Представьте, что вы бы объявили && метод следующим образом:
тогда всякий раз, когда ref имеет значение null, вы получите ошибку, потому что isSomething будет вызываться по нулевой ссылке перед передачей в &&. По этой причине фактическая декларация:
источник
Изучение примера поможет вам лучше понять разницу.
Давайте определим простую функцию, которая возвращает текущее время:
Теперь мы определим функцию по имени , которая печатает два раза с задержкой на секунду:
И один по значению :
Теперь давайте назовем каждого:
Результат должен объяснить разницу. Фрагмент доступен здесь .
источник
CallByName
вызывается при использовании иcallByValue
вызывается всякий раз, когда встречается инструкция.Например:-
У меня есть бесконечный цикл, т.е. если вы выполните эту функцию, мы никогда не получим
scala
подсказку.callByName
функция принимает вышеloop
методы в качестве аргумента , и он никогда не используется внутри своего тела.При выполнении
callByName
метода мы не находим никаких проблем (мы получаемscala
запрос назад), поскольку мы не используем функцию цикла внутриcallByName
функции.callByValue
функция принимает вышеloop
методы в качестве параметра в результате внутри функции или выражения вычисляется перед выполнением внешней функции тамloop
функциями , исполняемыми рекурсивно , и мы никогда не получитьscala
подсказки обратно.источник
Посмотри это:
y: => Int - это вызов по имени. То, что передается как вызов по имени, это add (2, 1). Это будет оцениваться лениво. Таким образом, вывод на консоль будет «mul», затем «add», хотя add, кажется, вызывается первым. Вызов по имени действует как передача указателя на функцию.
Теперь измените с y: => Int на y: Int. Консоль покажет «добавить», а затем «мул»! Обычный способ оценки.
источник
Я не думаю, что все ответы здесь дают правильное обоснование:
При вызове по значению аргументы вычисляются только один раз:
Вы можете видеть выше, что все аргументы оцениваются независимо от того, нужны ли они, обычно
call-by-value
могут быть быстрыми, но не всегда, как в этом случае.Если бы стратегия оценки была
call-by-name
тогда, то разложение было бы:как вы можете видеть выше, нам никогда не приходилось оценивать
4 * 11
и, следовательно, сохранять немного вычислений, которые иногда могут быть полезны.источник