Scala: требуется ли временная переменная для обработки возвращаемого значения кортежа?

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

var queue = immutable.Queue[T]( /* .. some content ..*/ )
while( !queue.isEmpty ) {
   val (record, tempQueue) = queue.dequeue
   queue = tempQueue
   doSomethingWith(record)
}

Есть ли какой-нибудь трюк, чтобы избежать временной переменной tempQueue и заставить Scala присвоить возвращаемое значение Queue непосредственно переменной цикла queue? Необходимость вводить дополнительный символ раздражает, плюс, по-видимому, может быть некоторое лишнее копирование (хотя это может быть оптимизировано, не уверен).

Редактировать 1: конечно, как указывает Ионут Г. Стэн, я могу пропустить сопоставление с образцом и самостоятельно разобрать возвращенную пару, например:

while( !queue.isEmpty ) {
   val pair = queue.dequeue
   queue = pair._2
   doSomethingWith(pair._1)
}

Поэтому я должен уточнить вопрос следующим образом: есть ли способ использовать синтаксический сахар сопоставления с образцом, чтобы сделать это более элегантно? Я надеялся на что-то вроде этого, что, к сожалению, не компилируется:

var queue = immutable.Queue[T]( /* .. some content ..*/ )
var record : A = _
while( !queue.isEmpty ) {
   (record, queue) = queue.dequeue
   doSomethingWith(record)
}

person Gregor Scheidt    schedule 20.12.2011    source источник


Ответы (7)


Если вы настаиваете на сохранении этой структуры (цикл while и т. д.), я не вижу, как вы можете сделать ее короче, за исключением, возможно,:

var queue = immutable.Queue[T]( /* some content */ )
while( !queue.isEmpty ) queue.dequeue match {
  case (record, tempQueue) =>
    queue = queue.dequeue
    doSomethingWith(record)
}

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

for(record <- queue) {
  doSomethingWith(record)
}

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

Спецификация языка Scala, раздел 4.1, также ясно: назначения в стиле сопоставления расширяются до val определений, т. е. они будут связывать новый идентификатор.

person Philippe    schedule 20.12.2011
comment
Проблема с упрощенными примерами для SO, такими как мой выше, заключается в том, что на самом деле вы часто хотите сделать что-то более сложное, например, не полностью слить очередь (в этом случае цикл for не будет работать). Соответствие, которое вы предлагаете, я думаю, фактически эквивалентно коду, который у меня был, и он по-прежнему использует временную переменную (я обновил свой вопрос, чтобы привести пример того, на что я надеялся :-). Я предполагаю, что ответ может заключаться в том, что нет никакого способа сделать это, поскольку кажется, что я не могу извлечь результаты сопоставления с образцом в уже существующие vars, а только в вновь определенные vals. - person Gregor Scheidt; 20.12.2011
comment
К сожалению, ссылка на соответствующий вопрос, который вы любезно предоставили, также является (отрицательным) ответом на этот вопрос. Я приму ваш ответ. Спасибо! - person Gregor Scheidt; 20.12.2011

С неизменяемыми структурами данных рекурсия — это способ FP делать что-то.

def foo[T](queue: immutable.Queue[T]) {
  if (!queue.isEmpty) {
    val (record, remaining) = queue.dequeue
    doSomethingWith(record)
    foo(remaining)
  }
}

foo(queue) в основном такой же, как queue foreach doSomethingWith, предложенный Брайаном Смитом.

person Dan Burton    schedule 20.12.2011

Вы используете цикл while с неизменяемым Queue. Почему бы не использовать более функциональный подход (поскольку у вас все равно есть неизменяемый Queue)?

Вы можете определить функцию, которую хотите запустить для каждого элемента в Queue, а затем использовать операцию коллекций (карту и т. д. в зависимости от желаемого возврата), чтобы применить ее.

E.G.

  import scala.collection.immutable._

  val q = Queue[(Int,Int)]((1,2),(3,4),(5,6))

  def doSomethingWith(a:(Int,Int)) = {
    a swap
  }

  //returns a new Queue with each tuple's elements swapped
  q map doSomethingWith

  //returns unit (so only useful if doSomethingWith has a side effect)
  q foreach doSomethingWith   
person Brian Smith    schedule 20.12.2011
comment
Вы правы, что это лучший подход для обработки всей очереди. Проблема с упрощенными примерами, такими как мой выше, заключается в том, что на самом деле я хочу сделать что-то более сложное, например, не полностью опустошая очередь (в этом случае map/clear не будет работать так легко). Однако можно разделить очередь с помощью предиката, а затем полностью очистить и отобразить одну из секций. Спасибо! - person Gregor Scheidt; 20.12.2011

Вы можете использовать элементы с префиксом _, которые доступны до Tuple22:

scala> val a = (1,2)
a: (Int, Int) = (1,2)

scala> a._1
res0: Int = 1

scala> a._2
res1: Int = 2
person Ionuț G. Stan    schedule 20.12.2011
comment
Спасибо, Ionut, это, конечно, правильно, но я надеялся, что есть способ использовать сопоставление с образцом, чтобы каким-то образом перезаписать значение уже существующей переменной queue при создании нового значения для record. Я расширил вопрос и сослался на ваш ответ. - person Gregor Scheidt; 20.12.2011

Вот совпадение с образцом, но оно по-прежнему вводит временные переменные.

while( !queue.isEmpty ) {
  queue = queue.dequeue match {
    case (t: T, q: immutable.Queue[T]) =>      
      doSomethingWith(t)
      q
  }
}
person sourcedelica    schedule 20.12.2011

Вот небольшой лайфхак:

queue forall doSomethingWith

предполагая, что doSomethingWith имеет тип T => Boolean

person Don Mackenzie    schedule 20.12.2011

В зависимости от условия вы можете сначала отфильтровать список или использовать takeWhile, а затем сопоставить полученный набор. Что-то типа

(queue takeWhile condition) foreach operation

or

(queue withFilter condition) foreach operation

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

(Nil /: queue)(<add element to accumulator if needed>) foreach operation
person Ozymandias    schedule 20.12.2011