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

Параметр имени-имени и анонимной функции

Что еще неясно, так это то, что преимущества параметров имени по сравнению с анонимными функциями с точки зрения ленивой оценки и других преимуществ, если таковые имеются:

def func1(a: => Int)
def func2(a: () => Int)

Когда я должен использовать первый и второй?

Это не копия В чем разница между = > ,() = > , и Unit = >

4b9b3361

Ответ 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.