[英]Is it possible to work with a list of generic values with different type parameters in Scala?
我要實現以下目標:
以可變的方式很容易做到:所有處理器都有一個通用的基類,它們聚合的不同類型的數據封裝在具體的實現中; 該界面僅包含2個功能-“查看輸入字符串並構建內部數據”和“使用內部數據處理輸入字符串”。
當我在Scala中編寫代碼時,我想知道是否存在一種純粹的功能方法。 問題在於,現在這些處理器的基本特征是通過其內部數據的類型來參數化的,並且似乎沒有一種方法可以列出不同種類的處理器。
這個問題可以在一個更簡單的情況下得到證明:說我會堅持使用可變方法,但是由於某種原因已經參數化了處理器從字符串中獲取的類型:
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
}
它不起作用,因為f.get(s)
返回Any
。 看起來我需要在Scala的類型系統中表達List(new F1(), new F2())
包含F[?]
,它們是不同的,但一致的是,如果我采用該列表的元素,它具有一些具體的價值它的類型參數,而f.get(s)
就是該類型,應該由f.aggregate()
接受。
最后,我想要這樣的東西(由於我不知道該怎么做,所以省略了):
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
}
}
問題:
另外,您可以使用抽象類型而不是泛型,因此:
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)
}
不確定,如果我誤解了正確的操作順序,但這可能是一個起點。
Edit Just注意到,我以前的解決方案過於冗長,不需要任何臨時數據結構。
我不確定,您所說的“純功能”是什么意思。 以下解決方案(如果它是您的問題的解決方案)是“純功能的”,因為它除了main
的最終println
調用外沒有其他副作用。
請注意, 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)
}
}
但是請注意,我仍然不確定您實際想要完成什么。 F
接口並沒有真正指定哪個信息在什么時候從哪種方法流到任何位置,因此:這仍然是最好的選擇。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.