Различие между методом и функцией в Scala

Я считал Функции Scala (часть Другого тура по Scala). В том сообщении он заявил:

Методы и функции не являются тем же самым

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

244
задан Peter Mortensen 25 May 2013 в 08:20
поделиться

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
66
ответ дан 23 November 2019 в 03:09
поделиться

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

Во-первых, давайте посмотрим, что нам говорит спецификация 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 ""

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

228
ответ дан 23 November 2019 в 03:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: