2011-01-18 5 views
5

Я новичок в scala и пытаюсь написать литерал функции, который проверяет, является ли заданное целое число нечетным или нет. моя первая попытка:Как упростить функцию словаря scala как это?

val isOdd = (x:Int) => (x & 1) == 1

он прекрасно работает, и, так как параметр х появляется только один раз в этой функции буквальный, я соблазн использовать «_» обозначения для упрощения его дальше, как это:

val isOdd = ((_:Int) & 1) == 1

однако на этот раз компилятор жалуется:

warning: comparing a fresh object using `==' will always yield false 
val isOdd = ((_:Int) & 1) == 1 

Что означает это предупреждение? почему компилятор распознает ((_ :Int) & 1) как новый объект, а не поразрядную операцию, которая приводит к значению? есть ли способ записать этот литерал функции, используя нотацию «_»?

+0

Просто используйте: 'вал нечетное =! even (_: Int) ' –

ответ

20

Проблема заключается в основном, что Scala должен сказать разницу между

val isOdd = ((_:Int) & 1) == 1 

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

val result = collection.map(_ + 1) 

где вы хотите только вещи внутри скобок быть лямбда

Scala решил, что при использовании подчеркивание, чтобы создать лямбда, что он собирается выбрать самый внутренний набор скобок как границы, что лямбда. Есть одно исключение: (_:Int) не считается ближайшими скобками, поскольку его цель состоит только в том, чтобы группировать объявление типа с помощью заполнителя _.

Следовательно:

val isOdd = ((_:Int) & 1) == 1 
      ^^^^^^^^^^^^^^ 
      this is the lambda 

val result = collection.map(_ + 1) 
          ^^^^^^^ 
          this is the lambda 

val result = collection.map((_ + 1)/2) 
          ^^^^^^^^ 
          this is the lambda 
          and the compiler can't infer the type of the _ 

val result = somemap.map((_ + 1)/2 * _) 
         ^^^^^^^^ 
         this is an inner lambda with one parameter 
         and the compiler can't infer the type of the _ 
         ^^^^^^^^^^^^^^^^^ 
         this is an outer lambda with one parameter 

Этот последний случай позволяет делать такие вещи, как

_.map(_ + 1) 

и что переводятся в

x => x.map(y=> y + 1) 
+1

+1 для удивительного использования ключа каретки (и хорошего ответа!) – Andy

+0

thx для уточнения правил о границе выражения лямбда! –

2

Что Scala делает это:

  • видит ((_:Int) & 1) и создает объект типа (Int) => Int, то есть, функция.
  • тогда применяется оператор сравнения == сравнить эту функцию к значению 1

функция не равна значению 1. Поэтому результат false, так что ваш код эквивалентен:

val isOdd = false 

Что вы можете сделать, это создать еще одну анонимную функцию, которая выполняет часть ваших вычислений == 1. Это некрасиво:

val isOdd = ((_: Int) & 1)(_: Int) == 1 

Это эквивалентно более многословным (и, возможно, легче понять):

val isOdd = (x: Int) => 1 == ((_: Int) & 1)(x) 
+5

Я не рекомендую ни одно из этих решений - они оба более сложны, и оба они создают лишнюю, ненужную лямбду. Stick с 'val isOdd = (x: Int) => (x & 1) == 1' –

+1

Конечно! Но ОП спросил конкретно, есть ли способ написать его «isOdd» лямбда, используя только «_». Я не думаю, что мы обсуждаем здесь хороший стиль. –

+0

Вы пропустили 'val isOdd = ((_: ​​Int) & 1) иThen (_ == 1)'. :-) –

7

Там вы идете:

val isOdd = ((_: Int) & 1) andThen (1 ==) 
+0

+1 за блестящую идею функционального состава :) –

10

только немного обмана:

val isOdd = (_: Int) % 2 == 1 

:-)

+2

+1 Это не обман. Фактически, я утверждаю, что использование '&' для этого - преждевременная оптимизация. Форма '%' понятна, и любой компилятор (или, может быть, JIT), который стоит его соли, увидит '% 2' и изменит его на' & 1', если он действительно быстрее в этой операционной среде. –

+0

Я согласен с тем, что в целом мы должны оставить эту оптимизацию для компиляторов; но здесь я действительно хочу понять обоснование этой лямбды. снова thx для уточнения правила круглых скобок относительно лямбда :) –

+0

@ KenBloom Я бы сказал, что использование лучшего алгоритма для работы - это не преждевременная оптимизация, это просто хорошее программирование, и я устаю от людей, использующих это обоснование для написания медленного кода (вообще говоря, это микро-пример, где разница, вероятно, незначительна). То, что вы можете сделать это более элегантно с другим механизмом, имеет все, что связано со стилем, и не связано с оптимизацией. – PlexQ

1

Другой подход

val isOdd = (_:Int).&(1) == 1 
Смежные вопросы