Является ли дизайн PartialFunction неэффективным? - PullRequest
13 голосов
/ 31 октября 2010

Я давно об этом думал.Я часто вижу этот шаблон:

if (pf.isDefinedAt(in)) pf(in)

Разбивая его на два отдельных вызова, все шаблоны, которые были оценены в #isDefinedAt, затем также оцениваются в #apply.Например:

object Ex1 {
  def unapply(in: Int) : Option[String] = {
    println("Ex1")
    if (in == 1) Some("1") else None
  }
}

object Ex2 {
  def unapply(in: Int) : Option[String] = {
    println("Ex2")
    if (in == 2) Some("2") else None
  }
}

val pf : PartialFunction[Int,String] = {
  case Ex1(result) => result
  case Ex2(result) => result
}

val in = 2

if (pf.isDefinedAt(in)) pf(in)

Что печатает

Ex1
Ex2
Ex1
Ex2
res52: Any = 2

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

PartialFunction # liftстрадает от той же двойной оценки:

scala> pf.lift(2)
Ex1
Ex2
Ex1
Ex2
res55: Option[String] = Some(2)

Есть ли способ условно вызвать функцию, если она определена без потенциального вызова всех ваших экстракторов дважды?

1 Ответ

17 голосов
/ 01 ноября 2010

Сейчас идет разговор об этом прямо сейчас в списке рассылки scala-internals .Мартин Одерский предложил новый тип: FunctionWithDefault.Мартин говорит не только о штрафных санкциях за время выполнения, но и о штрафных санкциях за время компиляции (раздувания файлов классов) за использование PartialFunction:

Во-первых, нам нужно сгенерировать код сопоставления с образцом дважды, один разв приложении, а затем снова в isDefinedAt.Во-вторых, нам также необходимо выполнить код дважды, сначала для проверки применимости функции, а затем для ее фактического применения.

Ответ на ваш вопрос по сути "да", и это поведение (из PartialFunction) также не изменится из-за проблем обратной совместимости (например, что если isDefinedAt(* побочные эффекты).

Предлагается новый тип, FunctionWithDefault не имеет isDefinedAt и имеет метод:

trait FunctionWithDefault[-I, +O] {
  def applyOrElse[OO >: O](i : I, default : I => OO) : OO
}

, который действует немного как Option s getOrElse method.

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

...