18

ほとんどのデータ処理は、コンポーネントのパイプラインとして想定でき、ある出力が別の入力にフィードされます。典型的な処理パイプラインは次のとおりです。

reader | handler | writer

この議論を始めるためのフォイルとして、各セグメントがオブジェクトであるこのパイプラインのオブジェクト指向の実装を考えてみましょう。オブジェクトにはとオブジェクトhandlerの両方への参照が含まれ、次のようなメソッドがあります。readerwriterrun

define handler.run:
  while (reader.has_next) {
    data = reader.next
    output = ...some function of data...
    writer.put(output)
  }

概略的には、依存関係は次のとおりです。

reader <- handler -> writer

ここで、リーダーとハンドラーの間に新しいパイプラインセグメントを挿入したいとします。

reader | tweaker | handler | writer

繰り返しますが、このOO実装では、はオブジェクトtweakerのラッパーになり、メソッドは次のようになります(疑似命令型コードでは)。readertweaker

define tweaker.has_next:
  return reader.has_next

define tweaker.next:
  value = reader.next
  result = ...some function of value...
  return result

これはあまり構成可能な抽象化ではないことがわかりました。いくつかの問題は次のとおりです。

  1. tweakerの左側でのみ使用できますhandler。つまり、上記の実装を使用tweakerしてこのパイプラインを形成することはできません。

    リーダー| ハンドラー| 微調整| 作家

  2. パイプラインの結合法則を利用したいので、このパイプラインは次のようになります。

    リーダー| ハンドラー| 作家

次のように表すことができます:

reader | p

pパイプラインはどこにありますかhandler | writer。このOO実装では、handlerオブジェクトを部分的にインスタンス化する必要があります

  1. (1)の言い換えでは、オブジェクトはデータを「プッシュ」するか「プル」するかを知る必要があります。

これらの問題に対処するデータ処理パイプラインを作成するためのフレームワーク(必ずしもOOではない)を探しています。

私はこれにタグを付けました。関数型プログラミングの概念がここで役立つかもしれないと感じたからですHaskellfunctional programming

目標として、次のようなパイプラインを作成できると便利です。

                     handler1
                   /          \
reader | partition              writer
                   \          /
                     handler2

いくつかの観点から、Unixシェルパイプは、次の実装上の決定により、これらの問題の多くを解決します。

  1. パイプラインコンポーネントは、別々のプロセスで非同期的に実行されます

  2. パイプオブジェクトは、「プッシャー」と「プラー」の間のデータの受け渡しを仲介します。つまり、データの書き込み速度が速すぎるライターと、読み取り速度が速すぎるリーダーをブロックします。

  3. 特別なコネクタ<を使用>し、パッシブコンポーネント(つまりファイル)をパイプラインに接続します

特に、エージェント間でのスレッド化やメッセージパッシングを使用しないアプローチに興味があります。これを行うにはおそらくそれが最善の方法ですが、可能であればスレッド化は避けたいと思います。

ありがとう!

4

4 に答える 4

22

ええ、はほぼ間違いなくあなたの男です。

質問で言っていることの種類に基づいて、あなたはHaskellにかなり慣れていないのではないかと思います。特に探しているものが「フレームワーク」である場合、矢印はおそらくかなり抽象的なように見えます。矢で何が起こっているのかを実際に理解するのに時間がかかったことを私は知っています。

そのため、そのページを見て「はい、それは私が望むもののように見えます」と言うと、問題を解決するために矢印を使用し始める方法についてかなり迷っていることに気付くかもしれません。だからここに少しのガイダンスがあるので、あなたはあなたが何を見ているのかを知っています。

矢印はあなたの問題を解決しません。代わりに、問題を表現するために使用できる言語を提供します。いくつかの事前定義された矢印が仕事をすることに気付くかもしれません-いくつかのクライスリ矢印-しかし、一日の終わりにあなたは表現する矢印(事前定義されたものはあなたにそれらを実装する簡単な方法を与えるだけです)を実装したいと思うでしょう「データプロセッサ」とはどういう意味ですか。ほとんど些細な例として、単純な関数でデータプロセッサを実装したいとします。あなたは書くでしょう:

newtype Proc a b = Proc { unProc :: a -> b }

-- I believe Arrow has recently become a subclass of Category, so assuming that.

instance Category Proc where
    id = Proc (\x -> x)
    Proc f . Proc g = Proc (\x -> f (g x))

instance Arrow Proc where
    arr f = Proc f
    first (Proc f) = Proc (\(x,y) -> (f x, y))

(***)これにより、さまざまな矢印コンビネータ、、などを使用するための機械と、(&&&)複雑(>>>)なことを行う場合にかなり便利な矢印表記が提供されます。したがって、Daniel Fischerがコメントで指摘しているように、質問で説明したパイプラインは次のように構成できます。

reader >>> partition >>> (handler1 *** handler2) >>> writer

しかし、すばらしいのは、プロセッサが何を意味するかはあなた次第だということです。異なるプロセッサタイプを使用して、同様の方法でスレッドをフォークする各プロセッサについて述べたことを実装することができます。

newtype Proc' a b = Proc (Source a -> Sink b -> IO ())

そして、コンビネータを適切に実装します。

これがあなたが見ているものです:プロセスの構成について話すための語彙。再利用するコードが少しありますが、主に、ドメインで役立つプロセッサの定義のためにこれらのコンビネータを実装するときに、思考を導くのに役立ちます。

私の最初の重要なHaskellプロジェクトの1つは、量子もつれの矢印を実装することでした。そのプロジェクトは、プログラミングのキャリアにおける大きなターニングポイントであるハスケルの考え方を本当に理解し始めたプロジェクトでした。たぶんあなたのこのプロジェクトはあなたのために同じことをするでしょうか?:-)

于 2011-11-15T23:30:16.520 に答える
7

遅延評価のおかげで、Haskellでは通常の関数合成の観点からパイプラインを表現できます。ファイル内の行の最大長を計算する例を次に示します。

main = interact (show . maximum . map length . lines)

ここにあるものはすべて、たとえばのような通常の関数です。

lines :: String -> [String]

しかし、遅延評価のおかげで、これらの関数は、UNIXパイプと同じように、入力を段階的に処理し、必要なだけ処理します。

于 2011-11-16T10:30:01.893 に答える
4

Haskellの列挙子パッケージはこのための優れたフレームワークです。次の3種類のオブジェクトを定義します。

  1. データをチャンクで生成する列挙子。
  2. データのチャンクを消費し、十分に消費した後に値を返すことを繰り返します。
  3. パイプラインの真ん中にあるものを列挙します。それらはチャンクを消費し、おそらく副作用を伴うチャンクを生成します。

これらの3種類のオブジェクトは、ストリーム処理パイプラインに構成されており、1つのパイプラインに複数の列挙子と反復を含めることもできます(1つが終了すると、次のパイプラインが実行されます)。これらのオブジェクトの1つを最初から作成するのは複雑な場合がありますが、通常の関数をデータストリームプロセッサに変換するために使用できるコンビネータはたくさんあります。たとえば、このパイプラインはstdinからすべての文字を読み取り、関数を使用してそれらを大文字に変換してから、toUpperstdoutに書き込みます。

ET.enumHandle stdin $$ ET.map toUpper =$ ET.iterHandle stdout

ここで、モジュールData.Enumerator.TextはとしてインポートされていETます。

于 2011-11-16T15:33:46.717 に答える
2

Yesodフレームワークは、コンジットパッケージの形式でHaskellパイプライブラリを利用します。

于 2012-10-10T07:34:42.830 に答える