[英]Is it possible to work with a list of generic values with different type parameters in Scala?
I want to achieve the following: 我要实现以下目标:
It's easy to do it in a mutable way: there's a common base class for all processors, the different kinds of data they aggregate is encapsulated in the concrete implementations; 以可变的方式很容易做到:所有处理器都有一个通用的基类,它们聚合的不同类型的数据封装在具体的实现中; the interface consists of just 2 functions --- "look at input string and build internal data" and "process input string using your internal data."
该界面仅包含2个功能-“查看输入字符串并构建内部数据”和“使用内部数据处理输入字符串”。
As I am writing it in Scala, I am wondering if there exists a pure functional approach. 当我在Scala中编写代码时,我想知道是否存在一种纯粹的功能方法。 The problem is that now the base trait for these processors is parameterized by the type of their internal data, and there doesn't seem to be a way to have a list of processors of different kinds.
问题在于,现在这些处理器的基本特征是通过其内部数据的类型来参数化的,并且似乎没有一种方法可以列出不同种类的处理器。
This problem can be demonstrated on a simpler case: say I'd stick with the mutable approach, but for some reason have parameterized the type of what the processor takes from the string: 这个问题可以在一个更简单的情况下得到证明:说我会坚持使用可变方法,但是由于某种原因已经参数化了处理器从字符串中获取的类型:
trait F[V] {
def get(line: String) : V
def aggregate(value: V)
def process(value: V) : String
}
class F1 extends F[Int] // ...
class F2 extends F[HashMap[Int, Int]] // ...
for (s <- List("string1", "string2");
f <- List(new F1(), new F2())
{
f.aggregate(f.get(s)); // Whoops --- doesn't work
}
It doesn't work because f.get(s)
returns Any
. 它不起作用,因为
f.get(s)
返回Any
。 Looks like I need to express in Scala's type system that List(new F1(), new F2())
contains F[?]
that are different but consistent in that if I take an element of that list, it has some concrete value of its type parameter, and f.get(s)
is of that type, which should be accepted by f.aggregate()
. 看起来我需要在Scala的类型系统中表达
List(new F1(), new F2())
包含F[?]
,它们是不同的,但一致的是,如果我采用该列表的元素,它具有一些具体的价值它的类型参数,而f.get(s)
就是该类型,应该由f.aggregate()
接受。
In the end, I would like to have something like this (with omissions because I don't get how to do it): 最后,我想要这样的东西(由于我不知道该怎么做,所以省略了):
trait F[D] {
def initData : D
def aggregate(line: String, data: D) : D
def process(line: String, data: D) : String
}
class F1 extends F[Int] // ...
class F2 extends F[HashMap[Int, Int]] // ...
// Phase 1
// datas --- List of f.initData, how to?
for (s <- List("string1", "string2")) {
for (f <- List(new F1(), new F2()) {
// let fdata be f's data
// update fdata with f.aggregate(s, fdata)
}
}
// Phase 2
for (s <- List("string1", "string2")) {
for (f <- List(new F1(), new F2()) {
// let fdata be f's data
// for all fs, concatenate f.process(s, fdata) into an output string
}
}
Questions: 问题:
Also, you may use abstract types instead of generics, so: 另外,您可以使用抽象类型而不是泛型,因此:
trait F {
type D
def initData: D
def aggregate(line: String, data: D): D
def process(line: String, data: D): String
}
class F1 extends F { type D = Int } // ...
class F2 extends F { type D = Map[Int, Int] } // ...
val strings = List("string1", "string2")
for (f <- List(new F1(), new F2())) {
val d = strings.foldLeft(f.initData) { (d, s) => f.aggregate(s, d) }
for (s <- strings)
f.process(s, d)
}
Don't sure, if I undrestood correct order of operation, but it may be a starting point. 不确定,如果我误解了正确的操作顺序,但这可能是一个起点。
Edit Just noticed, that my former solution was overly verbose, consing up a temporary data structure without any need. Edit Just注意到,我以前的解决方案过于冗长,不需要任何临时数据结构。
I am not sure, what you mean with "purely functional". 我不确定,您所说的“纯功能”是什么意思。 The following solution (if it is a solution to your problem) is "purely functional", as it has no side effects except the final
println
call in main
. 以下解决方案(如果它是您的问题的解决方案)是“纯功能的”,因为它除了
main
的最终println
调用外没有其他副作用。
Note, that the List[F[_]](...)
is important, since otherwise, the compiler will infer a very specific internal type for the elements of the list, which doesn't go well with the aggregateAndProcess
function. 请注意,
List[F[_]](...)
很重要,因为否则,编译器将为列表的元素推断出非常特定的内部类型,这与aggregateAndProcess
函数配合得并不好。
trait F[D] {
type Data = D // Abbreviation for easier copy+paste below. Does not
// contribute to the actual solution otherwise
def initData: Data
def aggregate(line: String, data: Data) : Data
def process(line: String, aggData: Data): String
}
class F1 extends F[Int] {
def initData: Data = 1
def aggregate(line: String, data: Data) : Data = data + 1
def process(line: String, aggData: Data): String = line + "/F1" + aggData
}
class F2 extends F[Boolean] {
def initData: Data = false
def aggregate(line: String, data: Data) : Data = !data
def process(line: String, aggData: Data): String = line + "/F2" + aggData
}
object Main {
private def aggregateAndProcess[T](line: String, processor: F[T]): String =
processor.process(line, processor.aggregate(line, processor.initData))
def main(args: Array[String]) {
val r = for {
s <- List("a", "b")
d <- List[F[_]](new F1, new F2)
} yield
aggregateAndProcess(s, d)
println(r.toList)
}
}
Note, though, that I am still unsure as to what you actually want to accomplish. 但是请注意,我仍然不确定您实际想要完成什么。 The
F
interface doesn't really specify, which information flows from which method into whatever location at what time, so: this is still a best-guess efford. F
接口并没有真正指定哪个信息在什么时候从哪种方法流到任何位置,因此:这仍然是最好的选择。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.