1

私は次のことを達成したい:

  1. 処理する必要がある文字列のリストがあります。
  2. これらのプロセッサにはいくつかの異なる種類があり、それぞれが文字列のどの部分を読み取るかを認識しています。
  3. 2 つのフェーズで作業する必要があります。まず、プロセッサは各入力文字列を確認して、プロセッサ固有のデータを構築する必要があります。次に、各入力文字列が各プロセッサによって処理され、結果の文字列が 1 つに結合されます。

変更可能な方法でそれを行うのは簡単です。すべてのプロセッサに共通の基本クラスがあり、それらが集約するさまざまな種類のデータが具体的な実装にカプセル化されます。このインターフェースは、「入力文字列を見て内部データを作成する」と「内部データを使用して入力文字列を処理する」という 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. 型理論や関数型プログラミング言語の背景知識がほとんどまたはまったくないと仮定して、それについて読むのに最適な場所はどこですか?
4

2 に答える 2

1

また、ジェネリックの代わりに抽象型を使用することもできます。

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)
}

操作の正しい順序を理解していないかどうかはわかりませんが、それが出発点になる可能性があります。

于 2013-02-25T22:23:13.980 に答える
0

編集私の以前のソリューションは過度に冗長であり、一時的なデータ構造を必要とせずに消費していることに気付きました。

「純粋に機能的」とはどういう意味かわかりません。次の解決策 (それが問題の解決策である場合) は、「純粋に機能的」ですprintln。.main

が重要であることに注意してください。そうしないと、コンパイラはリストの要素に対して非常に具体的な内部型を推測し、関数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どの情報がどのメソッドからどの場所にいつ流れるかを実際に指定していないため、これはまだ最善の推測です。

于 2013-02-25T21:22:14.230 に答える