Два способа определения функций в Scala. В чем разница?

Вот небольшой сеанс 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>

Здесь работает без _! В чем разница между функцией defed и функцией valed?


person aioobe    schedule 15.02.2011    source источник
comment
Рекс, я сэкономлю тебе немного времени. Вы уже ответили на этот вопрос в списке рассылки 27 января 2010 г. Это был хороший ответ, поэтому я добавил его в закладки. scala-programming-language.1934581. n4.nabble.com/   -  person Bradford    schedule 16.02.2011
comment
Я думаю, что есть некоторая досадная путаница в отношении использования термина функция/определение функции в Scala. Даже Programming in Scala говорит, что def определяет функцию, но это не совсем так, в зависимости от вашего определения функции. т.е. метод возвращает результат, который является функцией его параметров, но метод не является значением функции или объектом функции. Вероятно, было бы полезно различать эти два употребления термина функция.   -  person Knut Arne Vedaa    schedule 16.02.2011
comment
Прочитайте ответ ретронимов в моем аналогичном вопросе от апреля 10 года. stackoverflow.com/ вопросов/2720486/ - (ps, я QRd ваш аватар, очень смешно!)   -  person Synesso    schedule 16.02.2011
comment
Короче говоря, функция против метода   -  person Maxim    schedule 15.10.2014
comment
возможный дубликат функций и методов в Scala   -  person earldouglas    schedule 10.07.2015


Ответы (3)


Нет никакой разницы между функцией 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 классов. Метод — это дескриптор некоторого фрагмента кода, связанного с объектом.

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

person Daniel C. Sobral    schedule 16.02.2011
comment
Мне нравится этот ответ. Несколько дополнительных вопросов: 1) Вы видите тип X =› Y? Как насчет второй строки? Разве это не X => Y, потому что вокруг String есть ( и )? (Иначе мне пришлось бы искать офтамолога в желтых страницах.) 2) Из вашего описания мне кажется, что функции являются строго более общими, чем методы, и что я мог бы использовать ваши test1 -notation вместо test5-notation везде? В чем будет недостаток? - person aioobe; 16.02.2011
comment
@Synesso Да, вроде нет. Это представление REPL типа гипотетического метода. - person Daniel C. Sobral; 16.02.2011
comment
@aioobe Вторая строка не X => Y, потому что нет =>. Функции медленнее, чем методы, и они не могут получать параметры типа — это обсуждалось в другом месте в Stack Overflow. - person Daniel C. Sobral; 16.02.2011
comment
scala> val test4 = test2 это волонтер, или вы имели в виду вал test4 = test3? - person DenisFLASH; 11.03.2018

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

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

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

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

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

person axel22    schedule 15.02.2011
comment
Простой, но чистый ответ, +1 - person Muhammad Hewedy; 08.02.2013

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

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

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>

Поднятие этой функции приводит к другой функции. На этот раз типа () => (строка) => (строка)

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

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>
person Synesso    schedule 15.02.2011