簡體   English   中英

是否可以在Scala中使用具有不同類型參數的泛型列表?

[英]Is it possible to work with a list of generic values with different type parameters in Scala?

我要實現以下目標:

  1. 有一個我需要處理的字符串列表。
  2. 這些處理器有幾種,每種都知道要讀取字符串的哪一部分。
  3. 我需要分兩個階段進行工作:首先,處理器需要查看每個輸入字符串以構建處理器特定的數據; 第二,每個輸入字符串由每個處理器處理,然后將結果字符串合並為一個。

以可變的方式很容易做到:所有處理器都有一個通用的基類,它們聚合的不同類型的數據封裝在具體的實現中; 該界面僅包含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
  }
}

問題:

  1. 這個任務可以在Scala中以純功能方式解決嗎?
  2. 該任務可以用其他功能語言解決嗎?
  3. 這種情況看起來很普通。 我可以搜索的名字嗎?
  4. 假設幾乎沒有類型理論和函數式編程語言的背景,那么哪里是最好的閱讀指南?

另外,您可以使用抽象類型而不是泛型,因此:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM