2013-10-25 4 views
2

У меня возникли проблемы с пониманием подчеркивания в функциональных литералах.Почему переменная не может быть назначена заполнителем в функциональном литерале?

val l = List(1,2,3,4,5) 
l.filter(_ > 0) 

работает отлично

l.filter({_ > 0}) 

работает отлично

l.filter({val x=1; 1+_+3 > 0}) // ie you can have multiple statements in your function literal and use the underscore not just in the first statement. 

работает отлично

И еще:

l.filter({val x=_; x > 0}) 
e>:1: error: unbound placeholder parameter 
l.filter({val x=_; x > 0}) 

Я не могу присвоить _ переменного, даже если следующий юридическая функция буквальные:

l.filter(y => {val x=y; x > 0}) 

отлично работает.

Что дает? Является ли мой «val x = _» интерпретироваться как что-то еще? Благодаря!

+0

http://stackoverflow.com/a/16502936/1296806 Подобный вопрос. Это показывает, что синтаксис неверен. –

ответ

8

На самом деле, вам нужно сделать резервную копию шага.

Вы неправильно понимаете, как работают брекеты.

scala> val is = (1 to 5).toList 
is: List[Int] = List(1, 2, 3, 4, 5) 

scala> is map ({ println("hi") ; 2 * _ }) 
hi 
res2: List[Int] = List(2, 4, 6, 8, 10) 

Если println были частью функции передается map, вы бы видеть больше приветствий.

scala> is map (i => { println("hi") ; 2 * i }) 
hi 
hi 
hi 
hi 
hi 
res3: List[Int] = List(2, 4, 6, 8, 10) 

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

После того, как вы осознаете, что только результат expr имеет ожидаемый тип, который является ожидаемой функцией map, вы бы не подумали использовать подчеркивание в предыдущих утверждениях, поскольку голой знак подчеркивания нуждается в ожидаемом типе, подчеркивание.

Это система типа, сообщающая вам, что ваше подчеркивание находится не в нужном месте.

Приложение: в комментариях вы спросите:

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

Является ли это «немой» вопрос, простите за выражение?

Подчеркнуто, что вам не нужно указывать параметр, тогда вы говорите, что хотите назвать его.

Одним из вариантов использования может быть: есть несколько входящих параметров, но меня интересует наименование только одного из них.

Скала> (0 /: есть) (_ + _) res10: Int = 15

Скала> (0 /: есть) {случай (соотв, я) => ACC + 2 * I} res11: Int = 30

Это не работает, но можно задаться вопросом, почему. То есть мы знаем, что ожидает флеш, мы хотим применить что-то с arg. Какой аргумент? Независимо от того, что осталось после частично примененной частичной функции.

Скала> (0 /: есть) (({случай (_ я) => _ + 2 * я}) (_))

или

Скала> (0 /: есть) (((case (_, i) => val d = 2 * i; _ + 2 * d}) (_))

SLS 6.23 «Синтаксис заполнителя для анонимных функций» упоминает границу «expr» для когда вы должны знать, что представляет собой символ подчеркивания - это не объем как таковой. Если вы укажете тип подписок для подчеркивания, он все равно будет жаловаться на ожидаемый тип, по-видимому, потому, что вывод типа идет слева направо.

+0

Спасибо за ваш ответ. Вы совершенно правы - я думал, что блок, который я предоставил, был литералом функции. Вы прояснили это полностью. Должен признаться, что я не следую приложению, вероятно, потому, что мои навыки еще не до конца. Я перечитаю несколько раз и надеюсь, что получится. Спасибо! – Bruce

+0

Хм .. Означает ли это, что у вас никогда не может быть символа функции с использованием синтаксиса заполнителя подчёркивания, который содержит более одного утверждения ?! Если я делаю full-form: l.foreach ((x: Int) => {print ("Hi"); print (x)}), то я получаю Hi1Hi2Hi3, но если я делаю сокращенную форму, мой блок принимается за блок, а не ограничивает мою анонимную функцию: l.foreach ({print ("Hi"); print (_)}) дает Hi123. Как тогда я могу иметь более одного заявления в моем литеральном то есть. получить Hi1Hi2Hi3 с использованием сокращенного синтаксиса? – Bruce

+0

@Bruce На SO несколько ответов об этом синтаксисе попытайтесь дать эмпирическое правило; alexiv здесь называет это «десугарами до ближайшей сферы»; пункт 3 раздела 6.23 имеет два условия; это связано с запретом вложенных выражений. На практике вы ожидаете попробовать с выражением, и иногда это не работает. Один трюк, по крайней мере, состоит в том, чтобы изменить синтаксис оператора, например (x op_), может помочь распаковать expr (т. Е. Избавиться от parens). –

1

Потому что в этих двух случаях подчеркивание (_) означает две разные вещи. В случае функции это синтаксический сахар для лямбда-функции, ваш l.filter(_ > 0) позже desugares в l.filter(x => x > 0). Но в случае var оно имеет другое значение, а не функцию лямбда, но значение по умолчанию, и это поведение определяется только для var-х:

class Test { 
    var num: Int = _ 
} 

Здесь num собирается инициализировать его значение по умолчанию определяется его тип Int. Вы не можете сделать это с помощью val, потому что vals являются окончательными, и если в случае vars вы можете позже назначить им несколько разных значений, с vals это не имеет смысла.

Update

Рассмотрим следующий пример:

l filter { 
    val x = // compute something 
    val z = _ 
    x == z 
} 

Согласно вашей идее, z должен быть связан с первым аргументом, но как Scala должны понять это, или у вас есть больше кода в это вычисление, а затем подчеркивание.

Update 2

Существует вариант решетки в лестницу РЕПЛ: scala -Xprint:type. Если вы включите его и распечатать код (l.filter({val x=1; 1+_+3 > 0})), это то, что вы увидите:

private[this] val res1: List[Int] = l.filter({ 
    val x: Int = 1; 
    ((x$1: Int) => 1.+(x$1).+(3).>(0)) 
}); 

1+_+3 > 0 desugares в функцию: ((x$1: Int) => 1.+(x$1).+(3).>(0)) то, что фильтр на самом деле ожидает от вас, функцию от Int до Boolean.Ниже также работает:

l.filter({val x=1; val f = 1+(_: Int)+3 > 0; f})

причина е здесь частично применяется функция от Int к Boolean, но подчеркивание не присваивается первый аргумент, это desugares к области замыкается:

private[this] val res3: List[Int] = l.filter({ 
    val x: Int = 1; 
    val f: Int => Boolean = ((x$1: Int) => 1.+((x$1: Int)).+(3).>(0)); 
    f 
}); 
+0

var? оригинальный код не содержит никаких варов –

+0

@ om-nom-nom спасибо, я вижу. '_' не определен для vars. Это объяснение, когда вы можете использовать такой синтаксис – 4lex1v

+0

Ах спасибо за ваш ответ. Так есть способ, которым я могу назначить _ и использовать его в литерале функции? (конечно, я мог бы просто использовать синтаксис длинной руки, но я пытаюсь понять scala) – Bruce

1

синтаксис подчеркивание в основном пользователя для следующей замены:

coll.filter(x => { x % 2 == 0}); 
coll.filter(_ % 2 == 0); 

Это может заменить только один параметр. Это синтаксис заполнителя. Простой синтаксический сахар для лямбда.

В случае взлома вы пытаетесь инициализировать нуль/дефолт.

Для примитивных типов с инициализации конвенций:

var x: Int = _; // x will be 0 

Общий случай:

var y: List[String] = _; // y is null 
var z: Any = _; // z = null; 

Чтобы получить педантичный, он работает, потому что null является ссылка на единственный экземпляр scala.Null, суб- тип любого типа, который всегда будет удовлетворять типу, связанному из-за ковариации. Посмотрите HERE.

Сценарий очень общее использование, в ScalaTest:

class myTest extends FeatureTest with GivenWhenThen with BeforeAndAfter { 
    var x: OAuthToken = _; 
    before { 
     x = someFunctionThatReturnsAToken; 
    } 
} 

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

Компилятор даже не позволит вам: error: unbound placeholder parameter. Это ваш точный случай, компилятор думает, что вы по умолчанию, поведение не определено для val.

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

Для получения дополнительной информации о _ в Скале, посмотрите HERE.

+0

Спасибо, Флавиан. На самом деле, я думаю, вы, возможно, думаете, что я понимаю больше, чем я. Я просто пытался заставить параметр функции literal_ присваиваться переменной внутри литерала функции. (Я вообще не знал о синтаксисе var x = _ и не пытался пытаться что-то подобное с помощью val x = _. Я просто пытался получить x, чтобы иметь то же значение, что и параметр функции literal) – Bruce

+0

Спасибо - да, я говорю, что работает в моем первоначальном вопросе. Я спрашиваю - как я могу привязать параметр _ к переменной в литерале функции? Благодарю. – Bruce

+0

Ah ok - спасибо. – Bruce

Смежные вопросы