Подтвердить что ты не робот

Два способа определения функций в 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>

Здесь он работает без _! Какая разница между функцией def ed и функцией val ed?

4b9b3361

Ответ 1

Нет никакой разницы между 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 - это метод, который возвращает a java.lang.String. Кроме того, test1 через test4 не принимают параметры (возможно, только test1), тогда как test5 делает.

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

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

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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>

Поднятие этой функции приводит к другой функции. Это время типа() = > (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>