Параметр имени-имени и анонимной функции
Что еще неясно, так это то, что преимущества параметров имени по сравнению с анонимными функциями с точки зрения ленивой оценки и других преимуществ, если таковые имеются:
def func1(a: => Int)
def func2(a: () => Int)
Когда я должен использовать первый и второй?
Это не копия В чем разница между = > ,() = > , и Unit = >
Ответы
Ответ 1
В обоих случаях лень одинакова, но есть небольшие различия. Рассмотрим:
def generateInt(): Int = { ... }
def byFunc(a: () => Int) { ... }
def byName(a: => Int) { ... }
// you can pass method without
// generating additional anonymous function
byFunc(generateInt)
// but both of the below are the same
// i.e. additional anonymous function is generated
byName(generateInt)
byName(() => generateInt())
Функции с вызовом по имени, однако, полезны для создания DSL. Например:
def measured(block: ⇒ Unit): Long = {
val startTime = System.currentTimeMillis()
block
System.currentTimeMillis() - startTime
}
Long timeTaken = measured {
// any code here you like to measure
// written just as there were no "measured" around
}
Ответ 2
def func1(a: => Int) {
val b = a // b is of type Int, and it`s value is the result of evaluation of a
}
def func2(a: () => Int) {
val b = a // b is of type Function0 (is a reference to function a)
}
Ответ 3
Пример может дать довольно подробный обзор различий.
Учтите, что вы хотели написать собственную версию текущего цикла while
в Scala. Я знаю, я знаю... используя while
в Scala? Но дело не в функциональном программировании, это пример, который хорошо демонстрирует тему. Так что держись со мной. Мы будем называть нашу собственную версию whyle
. Кроме того, мы хотим реализовать его без использования Scala builtin while
. Чтобы вывести это, мы можем сделать нашу конструкцию whyle
рекурсивной. Кроме того, мы добавим аннотацию @tailrec
, чтобы убедиться, что наша реализация может использоваться как реальная замена встроенного while
. Здесь сначала идет:
@scala.annotation.tailrec
def whyle(predicate: () => Boolean)(block: () => Unit): Unit = {
if (predicate()) {
block()
whyle(predicate)(block)
}
}
Посмотрим, как это работает. Мы можем передать параметризованные кодовые блоки в whyle
. Первая - это параметрированная функция predicate
. Вторая - параметрированная функция block
. Как мы будем использовать это?
Мы хотим, чтобы наш конечный пользователь использовал whyle
так же, как и структуру управления while
:
// Using the vanilla 'while'
var i = 0
while(i < args.length) {
println(args(i))
i += 1
}
Но поскольку наши кодовые блоки параметризуются, конечный пользователь нашего цикла whyle
должен добавить некоторый уродливый синтаксический сахар, чтобы заставить его работать:
// Ouch, our whyle is hideous
var i = 0
whyle( () => i < args.length) { () =>
println(args(i))
i += 1
}
Итак. Похоже, что если мы хотим, чтобы конечный пользователь мог вызвать наш цикл whyle
в более привычном, родном стиле, нам нужно будет использовать бесцельные функции. Но тогда у нас действительно большая проблема. Как только вы будете использовать бесцельные функции, вы больше не сможете использовать свой торт и съесть его. Вы можете съесть только торт. Вот:
@scala.annotation.tailrec
def whyle(predicate: => Boolean)(block: => Unit): Unit = {
if (predicate) {
block
whyle(predicate)(block) // !!! THIS DOESN'T WORK LIKE YOU THINK !!!
}
}
Ого. Теперь пользователь может вызвать наш цикл whyle
так, как они ожидают... но наша реализация не имеет никакого смысла. У вас нет способа вызова функции без параметров и передачи функции в качестве значения. Вы можете назвать это. Это то, что я имею в виду, только съедая ваш торт. У тебя тоже не получится. И поэтому наша рекурсивная реализация теперь выходит из окна. Он работает только с параметризованными функциями, что, к сожалению, довольно уродливо.
На данный момент мы можем искушаться обманом. Мы могли бы переписать наш цикл whyle
для использования Scala встроенного while
:
def whyle(pred: => Boolean)(block: => Unit): Unit = while(pred)(block)
Теперь мы можем использовать наш whyle
точно так же, как while
, потому что нам нужно было только съесть наш торт... нам тоже не нужно было его иметь.
var i = 0
whyle(i < args.length) {
println(args(i))
i += 1
}
Но мы обманули! На самом деле, вот способ иметь нашу собственную оптимизированную по хвосту версию цикла while:
def whyle(predicate: => Boolean)(block: => Unit): Unit = {
@tailrec
def whyle_internal(predicate2: () => Boolean)(block2: () => Unit): Unit = {
if (predicate2()) {
block2()
whyle_internal(predicate2)(block2)
}
}
whyle_internal(predicate _)(block _)
}
Вы можете понять, что мы только что сделали? У нас есть исходные (но уродливые) параметризованные функции во внутренней функции. Мы завершаем функцию, которая принимает в качестве аргументов безпараметрические функции. Затем он вызывает внутреннюю функцию и преобразует беспараметрические функции в параметризованные функции (путем превращения их в частично прикладные функции).
Попробуй попробовать и посмотри, работает ли он:
var i = 0
whyle(i < args.length) {
println(args(i))
i += 1
}
И это так!
К счастью, поскольку в Scala у нас есть закрытия, мы можем очистить это большое время:
def whyle(predicate: => Boolean)(block: => Unit): Unit = {
@tailrec
def whyle_internal: Unit = {
if (predicate) {
block
whyle_internal
}
}
whyle_internal
}
Круто. В любом случае, это некоторые действительно большие различия между параметрируемыми и параметризованными функциями. Надеюсь, это даст вам некоторые идеи!
Ответ 4
Два формата используются взаимозаменяемо, но есть случаи, когда мы можем использовать только одну из тем.
объясним на примере, предположим, что нам нужно определить класс case с двумя параметрами:
{
.
.
.
type Action = () => Unit;
case class WorkItem(time : Int, action : Action);
.
.
.
}
как мы видим, второй параметр класса WorkItem имеет тип Action.
если мы попытаемся заменить этот параметр другим форматом = > ,
case class WorkItem1(time : Int, s : => Unit)
компилятор покажет сообщение об ошибке:
Несколько маркеров в этой строке:
Параметры `val 'могут не вызываться по вызову Параметр Call-by-name создание:() ⇒
так как мы видим, что формат () = > более общий, и мы можем использовать его для определения типа, как поля класса или параметра метода, с другой стороны = > может использоваться как параметр метода, но не как поле класса.
Тип по имени, в котором пустой список параметров,(), оставлен, только разрешено для параметров. Нет такой вещи, как переменная by-name или по-имени.
Ответ 5
Вы должны использовать первое определение функции, если хотите передать в качестве аргумента Int
по имени.
Используйте второе определение, если вы хотите, чтобы аргумент был функцией без параметров, возвращающей Int
.