Композиция функции Scala totalFn (partalFn (totalFn (x))) - PullRequest
1 голос
/ 19 марта 2019

Я пытался составить три функции, из которых только средняя была PartialFunction. Я ожидаю, что результирующий тип также будет PartialFunction.

Пример:

val mod10: Int => Int = _ % 10
val inverse: PartialFunction[Int, Double] = { case n if n != 0 => 1.0 / n }
val triple: Double => Double = _ * 3

val calc: Int => Double = mod10 andThen inverse andThen triple

Однако, calc не определен во всем его домене. Это вызовет MatchError для каждого числа, кратного 10.

В чем причина возврата полной функции, когда хотя бы одна из функций в композиции является частичной?

Другой пример, когда композиция частичных функций приводит к другой частичной функции с неверными условиями области:

val inverse: PartialFunction[Double, Double] = { case n if n != 0 => 1.0 / n }
val arcSin: PartialFunction[Double, Double] = { 
   case n if math.abs(n) <= 1 => math.asin(n)
}

val calc: PartialFunction[Double, Double] = inverse andThen arcSin

Я бы ожидал, что домен calc будет (-Infinity, -1] union [1, Infinity), но вызов calc.lift(0.5) вызовет MathError вместо возврата None, потому что вход находится в домене первой функции.

Спасибо, Норберт

Ответы [ 3 ]

1 голос
/ 19 марта 2019

Пример 1. Какова причина возврата функции итога, когда хотя бы одна из функций в композиции является частичной?

Это потому, что первая функция в вашем первом примере - это функция итога (Function1), а ее метод andThen возвращает Function1 независимо от того, является ли вторая функция полной или частичной:

def andThen[A](g: (R) => A): (T1) => A

Я предполагаю, что команда разработчиков языка Scala предпочитает более обобщенное возвращаемое значение, поскольку PartialFunction является подклассом функции и скорее позволит пользователям получать специализированный код по мере необходимости.

Пример 2: вызов calc.lift (0.5) вызовет MathError вместо возврата None

Из документа PartialFunction API doc , составление двух частичных функций через andThen вернет частичную функцию с тем же доменом, что и первая частичная функция:

 def andThen[C](k: (B) => C): PartialFunction[A, C]

Таким образом, результирующая составная функция не учитывает тот факт, что inverse(0.5) (т.е. 2.0) находится вне области второй частичной функции arcSin.


Итак, при составлении функции (полной или частичной) с частичной функцией с использованием andThen, как мы можем заставить ее возвращать частичную функцию с соответствующей областью?

Подобно тому, что продемонстрировано в этом SO Q & A , можно улучшить andThen с помощью пары неявных классов, чтобы ограничить область результирующей составной функции подмножеством области первой функции, которая возвращает значения в пределах области частичной функции:

object ComposeFcnOps {
  implicit class TotalCompose[A, B](f: Function[A, B]) {
    def andThenPartial[C](that: PartialFunction[B, C]): PartialFunction[A, C] =
      Function.unlift(x => Option(f(x)).flatMap(that.lift))
  }

  implicit class PartialCompose[A, B](pf: PartialFunction[A, B]) {
    def andThenPartial[C](that: PartialFunction[B, C]): PartialFunction[A, C] =
      Function.unlift(x => pf.lift(x).flatMap(that.lift))
  }
}

Тестирование с примерами функций:

import ComposeFcnOps._

val mod10: Int => Int = _ % 10
val inverse1: PartialFunction[Int, Double] = { case n if n != 0 => 1.0 / n }
val triple: Double => Double = _ * 3

val calc1 = mod10 andThenPartial inverse1 andThen triple
// calc1: PartialFunction[Int,Double] = <function1>

calc1.isDefinedAt(0)
// res1: Boolean = false

val inverse2: PartialFunction[Double, Double] = { case n if n != 0 => 1.0 / n }
val arcSin: PartialFunction[Double, Double] = { 
   case n if math.abs(n) <= 1 => math.asin(n)
}

val calc2 = inverse2 andThenPartial arcSin
// calc2: PartialFunction[Double,Double] = <function1>

calc2.isDefinedAt(0.5)
// res2: Boolean = false

calc2.lift(0.5)
// res3: Option[Double] = None
1 голос
/ 19 марта 2019

andThen определено на Function1 и просто не предназначено для составления частичных функций.Поэтому я рекомендую поднять их до общего количества функций перед его использованием.

val calc = Function.unlift(mod10 andThen inverse.lift andThen (_.map(triple)))

А

val calc = Function.unlift(inverse.lift andThen (_.flatMap(arcSin.lift)))
1 голос
/ 19 марта 2019

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

{ case n if n != 0 => 1.0 / n } 

тогда что если оно будет равно нулю, то это является причиной ошибки совпадения ..

{ 
   case n if n != 0 => 1.0 / n   // non-zero value.
   case n if n == 0 =>           // zero value.

} 

Надеюсь, это поможет.

...