Избегайте повторного выполнения ненужных вычислений в случае сопоставления с образцом - PullRequest
0 голосов
/ 26 апреля 2018

Эта проблема специфична для синтаксиса сопоставления с образцом в Scala.Допустим, у меня есть некоторый код, эквивалентный этому:

def process(seq: Seq[SomeObjectType]): SomeReturnType = seq match {
    case Seq() => // Return something
    case s if s exists (o => o.somePropertyTest) => {
        // Ok, the test is satisfied, now let's find
        // that object in the sequence that satisfies
        // it and do something with it
    }
    case _ => // Return something if no other case matches
}

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

def process(seq: Seq[SomeObjectType]): SomeReturnType = seq match {
    case Seq() => // Return something
    case s => {
        val obj = s find (o => o.somePropertyTest)
        if !obj.isEmpty {
            // Ok, the test is satisfied, and we have
            // the object that satisfies it, obj, so
            // do something with it directly
        } else {
            // Handle the no-match case here instead
        }
    }
}

Но это сводит на нет всю цель сопоставления с образцом, особенно когдаЕсть несколько случаев после того, где я делаю тест.Я все еще хочу, чтобы выполнение переходило к следующему случаю, если тест не пройден, т.е. если ни один элемент не удовлетворяет условию, но я также хочу «сохранить» элемент последовательности, найденный в тесте, и использовать его непосредственно втело дела, вроде как можно использовать @, чтобы дать имя сложному делу и использовать это имя в его теле.Возможно ли то, что я пытаюсь сделать с сопоставлением с образцом?Или это слишком сложно для сопоставления с образцом, чтобы иметь возможность его обрабатывать?

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

Ответы [ 2 ]

0 голосов
/ 26 апреля 2018

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

case class Selector[T](condition: T => Boolean) {
  def unapply(seq: Seq[T]): Option[T] = seq.find(condition)
}
object Selector {
  // seq argument is used only to infer type `T`
  def from[T](seq: Seq[T])(condition: T => Boolean): Selector[T] = Selector(condition)
}

def process(seq: Seq[SomeObjectType]): SomeReturnType = {
  // Create an extractor object instance
  val Select = Selector.from(seq)(_.somePropertyTest)

  seq match {
    case Seq() =>     // seq empty
    case Select(o) => // found o, we can process it now
    case _ =>         // didn't find a suitable element
  }
}  
0 голосов
/ 26 апреля 2018

Не проверено, но что-то вроде этого:

def process(seq: Seq[SomeObjectType]): SomeReturnType = seq match {
    case Seq() => // Return something
    case x +: xs if x.somePropertyTest == desiredProperty => something(x)
    case x +: xs => process(xs)
}

Это работает, потому что Seq является рекурсивной структурой, в которой процесс повторяется.

...