В Scala, в чем разница между использованием `_` и именованным идентификатором? - PullRequest
10 голосов
/ 01 марта 2010

Почему я получаю ошибку, когда пытаюсь использовать _ вместо именованного идентификатора?

scala> res0
res25: List[Int] = List(1, 2, 3, 4, 5)

scala> res0.map(_=>"item "+_.toString)
<console>:6: error: missing parameter type for expanded function ((x$2) => "item
 ".$plus(x$2.toString))
       res0.map(_=>"item "+_.toString)
                           ^

scala> res0.map(i=>"item "+i.toString)
res29: List[java.lang.String] = List(item 1, item 2, item 3, item 4, item 5)

Ответы [ 3 ]

18 голосов
/ 01 марта 2010

Символы подчеркивания, используемые вместо имен переменных, являются специальными; N-е подчеркивание означает N-й аргумент анонимной функции. Таким образом, следующие значения эквивалентны:

List(1, 2, 3).map(x => x + 1)

List(1, 2, 3).map(_ + 1)

Но, если вы сделаете это:

List(1, 2, 3).map(_ => _ + 1) 

Затем вы отображаете список с помощью функции, которая игнорирует единственный аргумент и возвращает функцию, определенную _ + 1. (Этот конкретный пример не будет компилироваться, потому что компилятор не может определить тип второго подчеркивания.) Эквивалентный пример с именованными параметрами будет выглядеть так:

List(1, 2, 3).map(x => { y => y + 1 })

Короче говоря, использование подчеркивания в списке аргументов функции означает: «Я игнорирую эти аргументы в теле этой функции». Использование их в теле означает «Компилятор, пожалуйста, сгенерируйте список аргументов для меня». Два использования не очень хорошо сочетаются.

4 голосов
/ 01 марта 2010

В дополнение к другим ответам, вот несколько примеров, показывающих, почему вы получаете «пропущенный тип параметра» в некоторых случаях при использовании «_» в качестве параметра-заполнителя.

Вывод типа Scala рассматривает «ожидаемый» тип выражения на основе его контекста. Если нет контекста, он не может определить тип параметров. Обратите внимание, что в сообщении об ошибке первый и второй экземпляры _ заменены на идентификаторы, сгенерированные компилятором x$1 и x$2.

scala> _ + _
<console>:5: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2))
       _ + _
       ^
<console>:5: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2))
       _ + _
           ^

Добавление атрибута типа ко всему выражению обеспечивает достаточный контекст, чтобы помочь логическому выводу:

scala> (_ + _) : ((Int, Int) => Int)
res3: (Int, Int) => Int = <function2>

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

scala> (_: Int) + (_: Int)          
res4: (Int, Int) => Int = <function2>

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

scala> def bar[A, R](a1: A, a2: A, f: (A, A) => R) = f(a1, a2)  
bar: [A,R](a1: A,a2: A,f: (A, A) => R)R

scala> bar[Int, Int](1, 1, _ + _)
res5: Int = 2

Однако, если мы попросим компилятор вывести параметры типа, в случае ошибки:

scala> bar(1, 1, _ + _)          
<console>:7: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2))
       bar(1, 1, _ + _)
                 ^
<console>:7: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2))
       bar(1, 1, _ + _)
                     ^

Мы можем помочь, хотя, путем каррирования списков параметров. Здесь аргументы первого списка параметров (1, 1) говорят о том, что параметр типа A должен быть Int. Затем он знает, что тип аргумента f должен быть (Int, Int) => ?), а тип возвращаемого значения R выводится как Int, результат сложения целых чисел. Вы увидите тот же подход, который использовался в Traversable.flatMap в стандартной библиотеке.

scala> def foo[A, R](a1: A, a2: A)(f: (A, A) => R) = f(a1, a2) 
foo: [A,R](a1: A,a2: A)(f: (A, A) => R)R

scala> foo[Int, Int](1, 1) { _ + _ }
res1: Int = 2

scala> foo(1, 1) { _ + _ }
res0: Int = 2
3 голосов
/ 01 марта 2010

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

res0.map("item "+_.toString)
...