Составление последовательности функций переменных типов в Scala

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

val f1: Function1[Int, Double] = _ / 2d
val f2: Function1[Double, BigDecimal] = x=>BigDecimal(x - 2.1)
val f3: Function1[BigDecimal, String] = _.toString

val chained = (f1 andThen f2 andThen f3)(_)

println(chained(10))

То, что я хочу, это функция f, которая принимает на вход Seq(f1, f2, ...) и возвращает их цепочку, где f1, f2, ...fn не все имеют одинаковый вход и одинаковые типы вывода Т. Но они составные, например, так:

f1: Function1[A,B]
f2: Function1[B,C]
f3: Function1[C,D]

тогда функция сцепления вернет функцию f: [A,D].

Спасибо, Z

1 ответ

Решение

Два предложения по решению здесь:

  1. Решение, которое требует специального вида списка, который может отслеживать все типы в цепочке функций.
  2. asInstanceOf решение, которое работает на обычных списках.

Отслеживание всех типов промежуточных результатов

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

sealed trait Func1List[-In, +Res] {
  def ::[I, O <: In](h: I => O): Func1List[I, Res] = ConsFunc1(h, this)
}
object Func1List {
  def last[In, Res](f: In => Res): Func1List[In, Res] = LastFunc1(f)
  def nil[A]: Func1List[A, A] = LastFunc1(identity)
}

case class LastFunc1[-In, +Res](f: In => Res) 
  extends Func1List[In, Res]
case class ConsFunc1[-In, Out, +Res](head: In => Out, tail: Func1List[Out, Res]) 
  extends Func1List[In, Res]

Теперь для Func1List мы можем определить функцию, которая объединяет все элементы:

def andThenAll[A, Z](fs: Func1List[A, Z]): A => Z = fs match {
  case LastFunc1(f) => f
  case c: ConsFunc1[A, t, Z] => c.head andThen andThenAll[t, Z](c.tail)
}

Небольшой тест:

val f1: Function1[Int, Double] = _ / 2d
val f2: Function1[Double, BigDecimal] = x => BigDecimal(x - 2.1)
val f3: Function1[BigDecimal, String] = _.toString

val fs = f1 :: f2 :: Func1List.last(f3)
val f = andThenAll(fs)

println(f(42)) // prints 18.9

Просто asInstanceOf все вещи

Несколько менее изысканное, но гораздо более короткое решение:

def andThenAll[X, Y](fs: List[_ => _]): X => Y = fs match {
  case Nil => (identity[X] _).asInstanceOf[X => Y]
  case List(f) => f.asInstanceOf[X => Y]
  case hd :: tl => hd match {
    case f: Function1[X @unchecked, o] => f andThen andThenAll[o, Y](tl)
  }
}

Это здесь также приводит к 18.9:

println(andThenAll[Int, String](List(f1, f2, f3))(42))
Другие вопросы по тегам