Разница между методом и функцией в Scala
Я прочитал Scala Функции (часть другого тура Scala). В этой статье он заявил:
Методы и функции - это не одно и то же.
Но он ничего не объяснил об этом. Что он пытался сказать?
Ответы
Ответ 1
У Jim это довольно много покрыто его сообщение в блоге, но я размещаю здесь брифинг для справки.
Во-первых, посмотрим, что нам сообщает спецификация 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
, включая тип и тело. Типом является Тип метода, а тело - выражение или блок. Это также реализовано на JVM с тем, что Java называет "методами".
Наконец, Анонимная функция представляет собой экземпляр Тип функции (т.е. экземпляр признака FunctionN
) и Значение метода - это одно и то же! Различие заключается в том, что значение метода создается из методов либо путем постфиксации подчеркивания (m _
- это значение метода, соответствующее "объявлению функции" (def
) m
), либо процессом, называемым eta-expand, который похож на автоматический приведение от метода к функции.
Вот что говорят спекуляции, поэтому позвольте мне поставить это на передний план: мы не используем эту терминологию! Это приводит к слишком большой путанице между так называемым "объявлением функции", которое часть программы (глава 4 - основные объявления) и "анонимная функция", которая является выражением, и "тип функции", который, а также тип - признак.
Терминология, приведенная ниже и используемая опытными программистами Scala, делает одно изменение терминологии спецификации: вместо объявления функции, мы говорим метод. Или даже объявление метода. Кроме того, мы отмечаем, что объявления значений и объявления переменных также являются практическими целями.
Итак, учитывая приведенное выше изменение терминологии, здесь можно найти практическое объяснение различия.
Функция - это объект, который включает в себя один из признаков 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
. Он противоречит параметрам, которые он получает, и ко-варианту на результат.
Эта дисперсия означает, что a Function1[Seq[T], String]
является подтипом Function1[List[T], AnyRef]
. Будучи подтипом, его можно использовать вместо него. Легко видеть, что если я назову 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
равен (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 ""
Я думаю, что это в значительной степени охватывает все, но я буду рад дополнить это ответами на любые вопросы, которые могут остаться.
Ответ 2
Одна большая практическая разница между методом и функцией - это то, что означает 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
Ответ 3
функция. Функция может быть вызвана со списком аргументов для создания результат. Функция имеет список параметров, тело и тип результата. Функции, являющиеся членами класса, признака или одиночного объекта, являются называемые методами. Функции, определенные внутри других функций, называются локальные функции. Функции с типом результата Unit называются процедурами. Анонимные функции в исходном коде называются функциональными литералами. Во время выполнения функциональные литералы создаются в объекты, называемые значения функции.
Программирование в Scala Second Edition.
Мартин Одерски - Лекс Лоун - Билл Веннерс
Ответ 4
Скажем, у вас есть список
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 с функцией Vs Function, использованием функции в качестве переменных, созданием функции, возвращающей функцию
Ответ 5
Функции не поддерживают параметры по умолчанию. Способы делают. Преобразование из метода в функцию теряет параметры по умолчанию. (Scala 2.8.1)
Ответ 6
Существует хорошая статья здесь, из которых большинство моих описаний принимаются. Просто краткое сравнение функций и методов моего понимания. Надеюсь, поможет:
Функции: они в основном объект. Точнее, функции - это объекты с методом 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>
Ответ 7
Вот отличный пост Роба Норриса, который объясняет разницу, вот TL; DR
Методы в Scala - это не значения, а функции. Вы можете создать функцию, которая делегирует методу через η -expansion (запускается с помощью символа подчеркивания в конце).
со следующим определением:
метод является то, что определяется с Защитой и значение то, что вы можете назначить на валу
В двух словах (выдержка из блога):
Когда мы определяем метод, мы видим, что мы не можем присвоить его 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
. Методы не являются значениями.
Однако, добавив постфиксный оператор η -expansion (η произносится как "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