с альтернативой в скале - PullRequest
8 голосов
/ 16 декабря 2011

Я пришел из Groovy, и у него есть метод .with для каждого типа, который принимает закрытие с одним аргументом; аргумент - это объект, для которого вызывается метод .with. Это позволяет использовать очень классную технику расширения функциональных возможностей цепочки, которая освобождает вас от необходимости вводить временные переменные, учитывает ваш код, облегчает чтение и выполняет другие тонкости.

Я хочу иметь возможность сделать что-то вроде этого:

Seq(1, 2, 3, 4, 5)
  .filter(_ % 2 == 0)
  .with(it => if (!it.isEmpty) println(it))

Вместо

val yetAnotherMeaninglessNameForTemporaryVariable = 
  Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0)
if (!yetAnotherMeaninglessNameForTemporaryVariable.isEmpty) 
  println(yetAnotherMeaninglessNameForTemporaryVariable)

Другими словами, в первом примере .with в некотором роде похож на .foreach, но вместо итерации по элементам объекта он вызывается один раз для самого объекта. Так что it равно Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0).

Так как я был очень удивлен, не обнаружив ничего подобного в Scala, мои вопросы:

  • я что-то упустил?
  • есть ли альтернативные методы, родные для Scala?
  • если нет, есть ли веские причины, по которым эта функция не реализована в Scala?

Обновление: Соответствующий запрос был опубликован в системе отслеживания проблем Scala: https://issues.scala -lang.org / browse / SI-5324 . Пожалуйста, проголосуйте и рекламируйте

Ответы [ 6 ]

12 голосов
/ 16 декабря 2011

В стандартной библиотеке такого метода не существует, но нетрудно определить свой собственный.

implicit def aW[A](a: A) = new AW(a)
class AW[A](a: A) {
  def tap[U](f: A => U): A = {
    f(a)
    a
  }
}

val seq = Seq(2, 3, 11).
          map(_ * 3).tap(x => println("After mapping: " + x)).
          filter(_ % 2 != 0).tap(x => println("After filtering: " + x))

РЕДАКТИРОВАТЬ: (в ответ на комментарий)

О, я неправильно понял. То, что вам нужно, есть в библиотеке Scalaz. Он приходит под именем |> (упоминается как оператор трубы). При этом ваш пример будет выглядеть так, как показано ниже:

Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) |> { it => if(!it.isEmpty) println(it) }

Если вы не можете использовать Scalaz, вы можете определить оператора самостоятельно:

implicit def aW[A](a: A) = new AW(a)
class AW[A](a: A) {
  def |>[B](f: A => B): B = f(a)
}

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

7 голосов
/ 16 декабря 2011

Существует некоторый синтаксис для этого шаблона, включенного в Scala:

Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) match { case it => if (!it.isEmpty) println(it) }

Однако это неприемлемая идиома, поэтому вам, возможно, следует воздерживаться от (ab) ее использования.

Если вам не нравится изобретать множество и множество имен для фиктивных переменных, помните, что вы можете использовать скобки:

val importantResult = {
  val it = Seq(1,2,3).filter(_ % 2 == 0)
  if (!it.isEmpty) println(it)
  it
}

val otherImportantResultWithASpeakingVariableName = {
  val it = // ...
  /* ... */
  it
}
3 голосов
/ 16 декабря 2011

Попробуйте что-то вроде этого.

println(Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0).ensuring(!_.isEmpty))

Выдает исключение подтверждения, если условие не выполняется.

1 голос
/ 17 декабря 2011

Это просто функциональное приложение f(x) перевернулось с ног на голову: x.with(f) ... Если вы ищете идиоматический способ выполнения with в Scala, откройте его:

(it => if (!it.isEmpty) println(it)) (Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0))

Точно так же, если вы хотите x.with(f).with(g), просто используйте g(f(x)) ...

1 голос
/ 17 декабря 2011

Хотя мне больше нравятся другие решения (так как они более локальны и поэтому им легче следовать), не забывайте, что вы можете

{ val x = Seq(1,2,3,4,5).filter(_ % 2 == 0); println(x); x }

чтобы избежать коллизий имен в ваших бессмысленных переменных и ограничить их до соответствующей области.

1 голос
/ 16 декабря 2011

Помните звонок по имени?Возможно, это дает вам необходимую емкость:

object Test {
 def main(args: Array[String]) {
   delayed(time());
 }

 def time() = {
    println("Getting time in nano seconds")
    System.nanoTime
 }

 def delayed( t: => Long ) = {
   println("In delayed method")
   println("Param: " + t)
   t
 }
}

, как описано в http://www.tutorialspoint.com/scala/functions_call_by_name.htm

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...