Различия между этими тремя способами определения функции в Scala

92

Даны три способа выражения одной и той же функции f(a) := a + 1:

val f1 = (a:Int) => a + 1
def f2 = (a:Int) => a + 1
def f3:(Int => Int) = a => a + 1

Чем отличаются эти определения? REPL не указывает очевидных различий:

scala> f1
res38: (Int) => Int = <function1>
scala> f2
res39: (Int) => Int = <function1>
scala> f3
res40: (Int) => Int = <function1>
qrest
источник
11
Вы должны заметить, что во втором блоке выше, оценка f1в REPL показывает значение, статически связанное с f1оценкой, f2и f3показывает результат вызова этих методов. В частности, новый Function1[Int, Int]экземпляр создается каждый раз , когда вызывается либо, f2либо f3, а f1остается неизменным Function1[Int, Int]навсегда.
Randall Schulz
@RandallSchulz, учитывая, что версия val не требует нового экземпляра функции, зачем использовать def в этом случае?
virtualeyes
2
@virtualeyes Единственная ситуация, которую я могу вспомнить, когда можно увидеть defs, дающие значения FunctionN [...], находится в библиотеке синтаксического анализатора комбинатора. Не очень часто писать методы, которые дают функции, и практически никогда нельзя использовать def для получения множества копий семантически / функционально неизменной функции.
Randall Schulz

Ответы:

112

f1 - функция, которая принимает целое число и возвращает целое число.

f2- это метод с нулевой арностью, который возвращает функцию, которая принимает целое число и возвращает целое число. (Когда вы f2печатаете в REPL позже, это становится вызовом метода f2.)

f3такое же, как f2. Вы просто не используете здесь вывод типов.

пропавший
источник
6
Почему f1это functionи f2есть method?
Freewind
17
@Freewind, функция - это объект с методом по имени apply. Метод, в общем, метод.
missingfaktor 07
Отличный ответ. Вопрос: вы говорите, что f2 имеет нулевую арность, но разве она не унарна? en.wikipedia.org/wiki/Arity «Функция с нулевым значением не принимает аргументов. Унарная функция принимает один аргумент». Просто любопытно!
Мэтью Корнелл
5
@MatthewCornell f2сам по себе не принимает аргументов. Возвращаемый им функциональный объект выполняет.
missingfaktor 08
122

Внутри класса valоценивается при инициализации, тогда defкак оценивается только тогда и каждый раз , когда вызывается функция. В приведенном ниже коде вы увидите, что x оценивается при первом использовании объекта, но не снова при обращении к члену x. Напротив, y не оценивается при создании экземпляра объекта, а оценивается каждый раз, когда к члену обращаются.

  class A(a: Int) {
    val x = { println("x is set to something"); a }
    def y = { println("y is set to something"); a }
  }

  // Prints: x is set to something
  val a = new A(1)

  // Prints: "1"
  println(a.x)

  // Prints: "1"                               
  println(a.x)

  // Prints: "y is set to something" and "1"                                  
  println(a.y)

  // Prints: "y is set to something" and "1"                                                                                   
  println(a.y)
Джек
источник
@JacobusR это правда только внутри класса?
Эндрю Кэссиди
например: scala> var b = 5 b: Int = 5 scala> val a: (Int => Int) = x => x + ba: Int => Int = <function1> scala> a (5) res48: Int = 10 scala> b = 6 b: Int = 6 scala> a (5) res49: Int = 11 Я ожидал, что a (5) вернет 10, а значение b будет встроено
Эндрю Кэссиди
@AndrewCassidy функция aнеизменяема и вычисляется при инициализации, но bостается изменяемым значением. Таким образом, ссылка на bустанавливается во время инициализации, но значение, сохраненное в, bостается изменяемым. Ради интереса теперь вы можете создать новый val b = 123. После этого вы a(5)всегда будете давать 11, так как bтеперь это совершенно новое значение.
Джек
@JacobusR спасибо ... в этом есть смысл. Это совпадает с определением «лексической области видимости», поскольку функция a несет ссылку на исходную «var b». Я думаю, что меня сбило с толку то, что говорят: var b = 5; val c = b; b = 6; действует иначе. Полагаю, мне не следует ожидать, что определение функции, которое содержит ссылки на исходную «лексическую» область видимости, будет вести себя так же, как Int.
Эндрю Кэссиди
3

Выполнение определения, такого как def x = e , не приведет к вычислению выражения e . Вместо этого e вычисляется всякий раз, когда используется x . В качестве альтернативы Scala предлагает определение значения val x = e , которое оценивает правую часть e как часть оценки определения. Если x затем используется впоследствии, он немедленно заменяется предварительно вычисленным значением e , так что выражение не нужно вычислять снова.

Scala на примере Мартина Одерски

Александр
источник