Как избежать дублирования вычислений в частичных функциях? - PullRequest
3 голосов
/ 18 июня 2020

Вот упрощенный фрагмент кода:

val pf = new PartialFunction[Int, Int]
{
    private def calc(x: Int): Int = x - 3

    override def isDefinedAt(x: Int): Boolean =
        {
            val result = calc(x)
            println(s"calc is called by isDefinedAt, result = $result")
            result > 0
        }

    override def apply(x: Int): Int =
        {
            val result = calc(x)
            println(s"calc is called by apply, result = $result")
            result
        }
}

(0 to 6).collect(pf)

Результат:

calc is called by isDefinedAt, result = -3
calc is called by isDefinedAt, result = -2
calc is called by isDefinedAt, result = -1
calc is called by isDefinedAt, result = 0
calc is called by isDefinedAt, result = 1
calc is called by apply, result = 1
calc is called by isDefinedAt, result = 2
calc is called by apply, result = 2
calc is called by isDefinedAt, result = 3
calc is called by apply, result = 3

Здесь метод cal c вызывается дважды для результат 1, 2, 3. Предположим, что метод cal c является дорогостоящим, тогда как избежать дублирования его вызовов при каждом вызове частичной функции pf ?

Ответы [ 3 ]

2 голосов
/ 18 июня 2020

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

Один из подходов - кэшировать входной аргумент x и результат метода calc. Это непросто сделать, это может привести к появлению серьезных ошибок, чрезмерному усложнению кода и, конечно, увеличению объема памяти.

Я бы сосредоточился на уменьшении сложности времени / памяти для метода calc, прежде чем рассматривать применение каких-либо решений для кеширования.

1 голос
/ 18 июня 2020

Возможно, попробуйте какую-нибудь форму мемоизации вот так

val pf = new PartialFunction[Int, Int] {
  private val expensiveCalc = (x: Int) => x - 3

  private val calc = new collection.mutable.WeakHashMap[Int, Int] {
    override def apply(a: Int) = getOrElseUpdate(a, expensiveCalc(a))
  }

  override def isDefinedAt(x: Int): Boolean = calc(x) > 0
  override def apply(x: Int): Int = calc(x)
}

(0 to 6).collect(pf)
1 голос
/ 18 июня 2020

Возможно, лучший способ - избежать использования partition function и просто использовать flatMap с Option для фильтрации не определенных входов в calc:

private def calc(x: Int): Int = x - 3

(0 to 6).flatMap{ x => Option(calc(x)).filter(_ > 0) }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...