Я считал Функции Scala (часть Другого тура по Scala). В том сообщении он заявил:
Методы и функции не являются тем же самым
Но он ничего не объяснил об этом. Что он пытался сказать?
Одно большое практическое различие между методом и функцией заключается в том, что означает 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
Джим подробно рассказал об этом в его сообщении в блоге , но я публикую здесь для справки брифинг.
Во-первых, давайте посмотрим, что нам говорит спецификация 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 , что является сокращением для признака Функция №
в стандартной библиотеке. Анонимные функции и Значения методов имеют типы функций, и типы функций могут использоваться как часть объявлений и определений значений, переменных и функций. Фактически, это может быть часть типа метода.
A Тип метода является типом без значения . Это означает, что нет значения - ни объекта, ни экземпляра - с типом метода. Как упоминалось выше, значение метода фактически имеет тип функции . Тип метода - это объявление def
- все о def
, кроме его тела.
Объявления и определения значений и Объявления и определения переменных - это объявления val
и var
, включая как тип , так и значение - которые могут быть, соответственно, Типом функции и Анонимными функциями или значениями метода . Обратите внимание, что в JVM эти (значения методов) реализуются с помощью того, что Java называет «методами».
Объявление функции - это объявление def
, включая тип и тело . Часть типа - это Тип метода, а тело - это выражение или блок . Это также реализовано на JVM с помощью того, что Java называет «методами».
Наконец, Анонимная функция является экземпляром Типа функции (т. Е. Экземпляром признака FunctionN
) и метода Значение то же самое! Различие заключается в том, что значение метода создается из методов либо путем добавления символа подчеркивания ( m _
- это значение метода, соответствующее «объявлению функции» ( def
) m
) или с помощью процесса, называемого eta-extension , который похож на автоматическое приведение от метода к функции.
Это то, что сказано в спецификации, поэтому позвольте мне сразу поставить: мы не используем эту терминологию! Это приводит к слишком большой путанице между так называемым «объявлением функции» , которое является частью программы (глава 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
. Это противоположный вариант по получаемым параметрам и ко-вариант по результату.
Это отклонение означает, что 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
будет (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 ""
Я думаю, что это в значительной степени охватывает все, но я буду счастлив дополнить это ответами на любые вопросы, которые могут остаться.