Два способа определения функций в Scala. В чем разница? - PullRequest
44 голосов
/ 15 февраля 2011

Вот небольшой сеанс Scala, который определяет и опробует некоторые функции:

scala> def test1(str: String) = str + str;    
test1: (str: String)java.lang.String

scala> test1("ab")
res0: java.lang.String = abab

прекрасно работает.

scala> val test2 = test1
<console>:6: error: missing arguments for method test1 in object $iw;
follow this method with `_' if you want to treat it as a partially applied function
       val test2 = test1
                   ^

упс.

scala> val test2 = test1 _
test2: (String) => java.lang.String = <function1>

scala> test2("ab")
res1: java.lang.String = abab

работает хорошо!

Теперь я видел синтаксис _ при свертывании (_ + _ и т. Д.).Итак, насколько я понимаю, _ в основном означает «аргумент».Так что test1 _ в основном означает функцию с аргументом, который присваивается test1 ". Но почему точно не совпадает с test1? Почему есть разница, если я добавлю_?

Так что я продолжал исследовать ...

scala> val test3 = (str: String) => str + str
test3: (String) => java.lang.String = <function1>

scala> test3("ab")
res2: java.lang.String = abab

scala> val test4 = test3
test4: (String) => java.lang.String = <function1>

Здесь это работает без _! В чем разница между def ed-функцией и * 1028?* ed функция?

Ответы [ 3 ]

58 голосов
/ 16 февраля 2011

def объявляет метод в окружающем объекте / классе / признаке, аналогично тому, как вы определяете методы в Java.Вы можете использовать def s только внутри других объектов / классов / черт.В REPL вы не можете видеть окружающий объект, потому что он «скрыт», но он существует.

Вы не можете присвоить def значению, потому что def не является значением - этометод в объекте.

(x: T) => x * x объявляет и создает экземпляр объекта функции , который существует во время выполнения.Объекты функций являются экземплярами анонимных классов, которые расширяют черты FunctionN.FunctionN черты идут с apply методом.Имя apply является особенным, потому что оно может быть опущено.Выражение f(x) разбито на f.apply(x).

Суть в том, что - поскольку функциональные объекты являются значениями времени выполнения, которые существуют в куче, вы можете назначить их значениям, переменным и параметрам или вернуть их из методов каквозвращаемые значения.

Чтобы решить проблему присвоения методов значениям (что может быть полезно), Scala позволяет использовать символ-заполнитель для создания объекта функции из метода.Выражение test1 _ в приведенном выше примере фактически создает функцию-оболочку для метода test1 - это эквивалентно x => test1(x).

53 голосов
/ 16 февраля 2011

Нет разницы между функцией def'ed и функцией val'ed:

scala> def test1 = (str: String) => str + str
test1: (String) => java.lang.String

scala> val test2 = test1
test2: (String) => java.lang.String = <function1>

scala> val test3 = (str: String) => str + str
test3: (String) => java.lang.String = <function1>

scala> val test4 = test2
test4: (String) => java.lang.String = <function1>

См? Все это функции, которые обозначены типом X => Y, который у них есть.

scala> def test5(str: String) = str + str
test5: (str: String)java.lang.String

Вы видите X => Y тип? Если вы это сделаете, иди к офтальмологу, потому что нет ни одного. Тип здесь (X)Y, обычно используется для обозначения метода .

На самом деле test1, test2, test3 и test4 - это все методы, которые возвращают функции. test5 - это метод, который возвращает java.lang.String. Кроме того, test1 - test4 не принимают параметры (в любом случае, только test1), в то время как test5.

Итак, разница довольно проста. В первом случае вы пытались назначить метод для val, но не указали параметры, которые принимает метод. Так что это не удалось, пока вы не добавили завершающее подчеркивание, что означало превратить мой метод в функцию .

Во втором примере у вас была функция, поэтому вам больше ничего не нужно было делать.

Метод не является функцией, и наоборот. Функция является объектом одного из FunctionN классов. Метод является дескриптором некоторого фрагмента кода, связанного с объектом.

См. Различные вопросы о методах и функциях в переполнении стека.

11 голосов
/ 16 февраля 2011

Подчеркивание означает разные вещи в разных контекстах. Но это всегда можно представить как вещь, которая здесь идет, но не обязательно должна называться .

При применении вместо параметров эффект состоит в том, чтобы поднять метод до функции.

scala> def test1(str: String) = str + str; 
test1: (str: String)java.lang.String

scala> val f1 = test1 _
f1: (String) => java.lang.String = <function1>

Обратите внимание, что метод стал функцией типа (String) => String.

Различие между методом и функцией в Scala заключается в том, что методы похожи на традиционные методы Java. Вы не можете передать их как ценности. Однако функции являются значениями сами по себе и могут использоваться в качестве входных параметров и возвращаемых значений.

Подъем может идти дальше:

scala> val f2 = f1 _
f2: () => (String) => java.lang.String = <function0>

Отмена этой функции приводит к другой функции. На этот раз type () => (String) => (String)

Из того, что я могу сказать, этот синтаксис эквивалентен явной замене всех параметров подчеркиванием. Например:

scala> def add(i: Int, j: Int) = i + j
add: (i: Int,j: Int)Int

scala> val addF = add(_, _)
addF: (Int, Int) => Int = <function2>

scala> val addF2 = add _    
addF2: (Int, Int) => Int = <function2>
...