Можно ли использовать ленивые значения в выражениях for? - PullRequest
2 голосов
/ 22 сентября 2011

У меня есть список Chicken с и Egg с, которые я хочу создать. Они определены как:

class Chicken (val name: String, e: => Egg) { lazy val child = e }

class Egg (val name: String, c: => Chicken) { lazy val parent = c }

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

  def fillBarn {
    lazy val chicken: Chicken = new Chicken("abc", egg)
    lazy val egg: Egg = new Egg("def", chicken)
  }

У меня есть список куриных / яичных имен, которые я хочу создать. К сожалению, следующее не компилируется:

val names = List("C1 E1", "C2 E2", "C3 E3")
val list = for {
  Array(cn, en) <- names.map(_.split(" "))
  lazy c: Chicken = new Chicken(cn, e)
  lazy e: Egg = new Egg(en, c)
} yield (c, e)

но без сахара:

val list = names.map(_.split(" ")).map { 
  case Array(cn, en) => 
    lazy val c: Chicken = new Chicken(cn, e)
    lazy val e: Egg = new Egg(en, c)
    (c, e)
}

Теперь, возможно, в этом простом случае лучше без for-expression, но если бы я действительно хотел использовать for-expression, могу ли я?

Я также понимаю, что в этом тривиальном случае я мог бы создать экземпляры Chicken и Egg в блоке yield, но в общем случае это не будет так, скажем, если бы я хотел выполнить дополнительную фильтрацию и отображение на основании экземпляров.

1 Ответ

5 голосов
/ 22 сентября 2011

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

def fillBarn(c: String, e: String) = {
  lazy val chicken: Chicken = new Chicken(c, egg)
  lazy val egg: Egg = new Egg(e, chicken)
  (chicken, egg)
}

, а затем

val list = for {
  Array(cn, en) <- names.map(_.split(" "))
  (c, e) = fillBarn(cn, en)
} yield (c, e)

Конечно, если вы хотите, нет необходимости определять метод fillBarn. Вы также можете сделать это в строке:

val list = for {
  Array(cn, en) <- names.map(_.split(" "))
  (c, e) = {
    lazy val chicken: Chicken = new Chicken(cn, egg)
    lazy val egg: Egg = new Egg(en, chicken)
    (chicken, egg)
  }
} yield (c, e)

Общая структура оператора for в Scala фиксирована. Существует только flatMap / map / foreach с <- или прямое присвоение имени новой переменной для последующего использования с =. Но в правой части этих операторов вы можете поместить все, что вам нравится, внутри блока, если этот блок возвращает соответствующий объект.

...