Разница между методом и функцией в Scala - PullRequest
241 голосов
/ 27 марта 2010

Я прочитал Функции Scala (часть Еще один тур по Scala ) В этом посте он заявил:

Методы и функции не одно и то же

Но он ничего не объяснил по этому поводу. Что он пытался сказать?

Ответы [ 7 ]

225 голосов
/ 27 марта 2010

Джим довольно подробно рассказал об этом в своем блоге , но я публикую здесь брифинг для справки.

Для начала посмотрим, что нам скажет спецификация Scala. Глава 3 (типы) рассказывает нам о типах функций (3.2.9) и типах методов (3.3.1). В главе 4 (основные декларации) говорится о декларации и определениях значений (4.1), декларации и определениях переменных (4.2) и объявлениях и определениях функций (4.6). Глава 6 (выражения) говорит о анонимных функциях (6.23) и значениях методов (6.7). Любопытно, что о значениях функций говорится один раз в 3.2.9, и нигде больше.

A Тип функции - это (примерно) тип формы (T1, ..., Tn) => U , что является сокращением для черты FunctionN в стандартной библиотеке. Анонимные функции и Значения метода имеют типы функций, и типы функций могут использоваться как часть объявлений и определений значений, переменных и функций. Фактически, он может быть частью типа метода.

A Тип метода является ненулевым типом . Это означает, что нет значение - без объекта, без экземпляра - с типом метода. Как упоминалось выше, Значение метода на самом деле имеет Тип функции . Тип метода - это объявление def - все о def, кроме его тела.

Объявления и определения значений и Объявления и определения переменных являются val и var объявлениями, включая как тип, так и значение - которые могут быть, соответственно Тип функции и Анонимные функции или значения методов . Обратите внимание, что в JVM эти (значения методов) реализованы с помощью того, что Java называет «методами».

A Объявление функции - это объявление def, включая type и body . Часть type является типом метода, а тело является выражением или блоком . Это также реализовано в JVM с помощью того, что Java называет «методами».

Наконец, Анонимная функция является экземпляром Типа функции (т. Е. Экземпляром признака FunctionN) и Значение метода это то же самое! Различие заключается в том, что значение метода создается из методов либо путем добавления нижнего подчеркивания (m _ - это значение метода, соответствующее «объявлению функции» (def) m), либо с помощью процесса, называемого Эта-расширение , которое похоже на автоматическое приведение метода к функции.

Так говорят спецификации, поэтому позвольте мне сказать об этом заранее: мы не используем эту терминологию! Это приводит к слишком большой путанице между так называемым "объявлением функции" , которая является частью программы (глава 4 - основные объявления) и «анонимная функция» , которая является выражением, и «тип функции» , которая является, ну вид - черта характера.

Приведенная ниже терминология, используемая опытными программистами Scala, вносит одно изменение в терминологию спецификации: вместо объявления функции , мы говорим method . Или даже объявление метода. Кроме того, мы отмечаем, что объявления значений и объявления переменных также являются методами для практических целей.

Итак, учитывая вышеуказанное изменение терминологии, вот практическое объяснение различия.

A function - это объект, который включает в себя одну из FunctionX черт, таких как Function0, Function1, Function2 и т. Д. Также может включать в себя PartialFunction, что на самом деле распространяется Function1.

Давайте посмотрим тип подписи для одной из этих черт:

trait Function2[-T1, -T2, +R] extends AnyRef

У этой черты есть один абстрактный метод (также есть несколько конкретных методов):

def apply(v1: T1, v2: T2): R

И это говорит нам все, что нужно знать об этом. Функция имеет метод apply, который получает N параметров типов T1 , T2 , ..., TN и возвращает что-то типа R. Он противоречит вариантам, которые он получает, и ко-вариантам - результат.

Эта разница означает, что Function1[Seq[T], String] является подтипом Function1[List[T], AnyRef]. Подтип означает, что его можно использовать вместо it. Легко видеть, что если я собираюсь позвонить f(List(1, 2, 3)) и ожидать AnyRef назад, то подойдет любой из двух приведенных выше типов.

Теперь, каково сходство метода и функции? Хорошо, если f является функцией, а m является методом, локальным для области действия, то оба могут быть вызваны следующим образом:

val o1 = f(List(1, 2, 3))
val o2 = m(List(1, 2, 3))

Эти вызовы на самом деле разные, потому что первый - просто синтаксический сахар. Scala расширяет его до:

val o1 = f.apply(List(1, 2, 3))

Что, конечно, является вызовом метода для объекта f. У функций также есть другие синтаксические сахара в качестве преимущества: функциональные литералы (фактически два) и сигнатуры типа (T1, T2) => R. Например:

val f = (l: List[Int]) => l mkString ""
val g: (AnyVal) => String = {
  case i: Int => "Int"
  case d: Double => "Double"
  case o => "Other"
}

Другое сходство между методом и функцией заключается в том, что первое можно легко преобразовать во второе:

val f = m _

Scala будет расширяться , что , предполагая, что m type равен (List[Int])AnyRef в (Scala 2.7):

val f = new AnyRef with Function1[List[Int], AnyRef] {
  def apply(x$1: List[Int]) = this.m(x$1)
}

В Scala 2.8 фактически используется класс AbstractFunction1 для уменьшения размеров классов.

Обратите внимание, что нельзя преобразовать наоборот - из функции в метод.

Методы, однако, имеют одно большое преимущество (ну, два - они могут быть немного быстрее): они могут получать параметры типа . Например, в то время как f выше может обязательно указывать тип List, который он получает (List[Int] в примере), m может параметризовать его:

def m[T](l: List[T]): String = l mkString ""

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

65 голосов
/ 27 марта 2010

Одно большое практическое различие между методом и функцией заключается в том, что означает return. return только когда-либо возвращается из метода. Например:

scala> val f = () => { return "test" }
<console>:4: error: return outside method definition
       val f = () => { return "test" }
                       ^

При возврате из функции, определенной в методе, выполняется нелокальный возврат:

scala> def f: String = {                 
     |    val g = () => { return "test" }
     | g()                               
     | "not this"
     | }
f: String

scala> f
res4: String = test

Принимая во внимание, что возвращение из локального метода возвращает только из этого метода.

scala> def f2: String = {         
     | def g(): String = { return "test" }
     | g()
     | "is this"
     | }
f2: String

scala> f2
res5: String = is this
34 голосов
/ 02 июля 2014

function Функция может быть вызвана со списком аргументов для получения результат. Функция имеет список параметров, тело и тип результата. Функции, которые являются членами класса, свойства или одноэлементного объекта называется методы . Функции, определенные внутри других функций, называются местные функции. Функции с типом результата Unit называются процедурами. Анонимные функции в исходном коде называются функциональными литералами. Во время выполнения функциональные литералы создаются в объектах, называемых значения функции.

Программирование в Scala Second Edition. Мартин Одерски - Лекс Ложка - Билл Веннерс

29 голосов
/ 01 ноября 2013

Скажем, у вас есть список

scala> val x =List.range(10,20)
x: List[Int] = List(10, 11, 12, 13, 14, 15, 16, 17, 18, 19)

Определить метод

scala> def m1(i:Int)=i+2
m1: (i: Int)Int

Определить функцию

scala> (i:Int)=>i+2
res0: Int => Int = <function1>

scala> x.map((x)=>x+2)
res2: List[Int] = List(12, 13, 14, 15, 16, 17, 18, 19, 20, 21)

Метод принятия аргумента

scala> m1(2)
res3: Int = 4

Определение функции с помощью val

scala> val p =(i:Int)=>i+2
p: Int => Int = <function1>

Аргумент для функции необязателен

 scala> p(2)
    res4: Int = 4

scala> p
res5: Int => Int = <function1>

Аргумент к методу обязателен

scala> m1
<console>:9: error: missing arguments for method m1;
follow this method with `_' if you want to treat it as a partially applied function

Проверьте следующее Учебное пособие , которое объясняет передачу других различий с примерами, такими как другой пример diff с методом против функции, Использование функции в качестве переменных, создание функции, возвращающей функцию

12 голосов
/ 04 мая 2011

Функции не поддерживают параметры по умолчанию. Методы делают. Преобразование метода в функцию теряет значения параметров по умолчанию. (Scala 2.8.1)

6 голосов
/ 23 февраля 2018

Здесь есть хорошая статья , из которой взято большинство моих описаний. Просто краткое сравнение функций и методов моего понимания. Надеюсь, это поможет:

Функция : Они в основном объект. Точнее, функции - это объекты с методом apply; Таким образом, они немного медленнее, чем методы из-за их накладных расходов. Это похоже на статические методы в том смысле, что они не зависят от вызываемого объекта. Простой пример функции похож на приведенный ниже:

val f1 = (x: Int) => x + x
f1(2)  // 4

Строка выше - это ничто иное, как присвоение одного объекта другому, например, object1 = object2. На самом деле object2 в нашем примере является анонимной функцией, и левая сторона получает тип объекта из-за этого. Следовательно, теперь f1 является объектом (функцией). Анонимная функция на самом деле является экземпляром Function1 [Int, Int], что означает функцию с 1 параметром типа Int и возвращаемым значением типа Int. Вызов f1 без аргументов даст нам подпись анонимной функции (Int => Int =)

Методы : Они не являются объектами, но присваиваются экземпляру класса, то есть объекту. Точно так же, как метод в Java или функции-члены в C ++ (как Раффи Хачадурян указал в комментарии к этот вопрос ) и т. Д. Простой пример метода похож на приведенный ниже:

def m1(x: Int) = x + x
m1(2)  // 4

Строка выше - не простое присвоение значения, а определение метода. Когда вы вызываете этот метод со значением 2, как во второй строке, x заменяется на 2, и результат будет вычислен, и вы получите 4 в качестве вывода. Здесь вы получите ошибку, если просто напишите m1, потому что это метод и нужно ввести значение. Используя _, вы можете назначить метод такой функции, как показано ниже:

val f2 = m1 _  // Int => Int = <function1>
1 голос
/ 24 апреля 2019

Вот отличный пост Роба Норриса, который объясняет разницу, вот TL; DR

Методы в Scala - это не значения, а функции. Вы можете создать функцию, которая делегирует методу через η-разложение (запускаемое завершающим подчеркиванием).

со следующим определением:

a метод - это то, что определено с def , а значение - это то, что вы можете присвоить val

В двух словах ( выдержка из блога ):

Когда мы определяем метод, мы видим, что мы не можем присвоить его val.

scala> def add1(n: Int): Int = n + 1
add1: (n: Int)Int

scala> val f = add1
<console>:8: error: missing arguments for method add1;
follow this method with `_' if you want to treat it as a partially applied function
       val f = add1

Обратите внимание также на тип из add1, который не выглядит нормально; вы не можете объявить переменную типа (n: Int)Int. Методы не являются значениями.

Однако, добавив постфиксный оператор η-расширения (η произносится как «eta»), мы можем превратить метод в значение функции. Обратите внимание на тип f.

scala> val f = add1 _
f: Int => Int = <function1>

scala> f(3)
res0: Int = 4

Эффект _ заключается в выполнении эквивалента следующего: мы создаем экземпляр Function1, который делегирует нашему методу.

scala> val g = new Function1[Int, Int] { def apply(n: Int): Int = add1(n) }
g: Int => Int = <function1>

scala> g(3)
res18: Int = 4
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...