4

やっと Haskell プログラムをリグすることができたので、最後に

  let foo = bar :: A

次に、1つの動作を取得します。

  let foo = bar :: B

次に、他の望ましい動作を取得します。

プログラムが実行時にこの引数を解析できるようにしたいのですが、どうすればよいかわかりません。何かアドバイス?


編集:仕様/フォーマットを自由に作成できるある種の(テキスト)構成ファイルを解析したいと思います。
考えられるおもちゃの例の 1 つは、構成ファイルで提供されるさらなるコンテキストに応じて、整数を Int または Double として読み取ることです。構成ファイルの次の行に沿ったものです。

barType: Int
barValue: 2

bar = 2 :: Int、および

barType: Double
barValue: 2

私に bar = 2 :: Double を与えます。ここでは、Num インスタンスを持つ任意の型を受け入れることができる場合があります。
私の場合、いくつかのメソッドを持つ型クラスがあり、その型クラスのインスタンスで何かを解析したいと考えています。メソッドは、正確なタイプに応じて、大幅に異なることを行う可能性があります。そのための Read インスタンスを作成する方法がわかりません。

ありがとう。

4

4 に答える 4

3

これを行っている場合は、内容が foo の型に依存しているが、戻り値の型が依存していないコードがあると思います。あなたの完全なコード例が

let foo = bar :: Int
in print (foo + 1)

通常、実行時のパラメーターの受け渡しは関数によって行われるため、最初にコードを関数として書き直します。型シグネチャの(Show a, Num a) =>は、一部の型クラス メソッドが隠しパラメーターとして関数に渡されることを示しています。

printBar :: (Show a, Num a) => a -> IO ()
printBar x = print ((bar `asTypeOf` x) + 1)

たとえば、本体でprintBar (undefined :: Double)type を使用します。Doubleコンパイラは、引数の型が であることを検出Doubleし、型クラス メソッドを に渡しDoubleます。

では、 の実際の型を選択する関数を書きましょうprintBar

invokeBar :: Bool -> (forall a. (Show a, Num a) => a -> b) -> b
invokeBar True  f = f (undefined :: Int)
invokeBar False f = f (undefined :: Double)

invokeBar True printBarまたはを呼び出して、 または を使用するかどうかを選択できるようにinvokeBar False printBarなりました。IntDouble

すべての可能なタイプを網羅するファイル形式がないため、ファイルから「不明なタイプ」を読み取ることはできません。ただし、使用する型の名前を認識するパーサーを作成し、それらの名前ごとに上記のアプローチを使用できます。

于 2012-06-21T13:35:30.737 に答える
2

インスタンス化される型に基づいてさまざまな動作を探している場合は、型クラスが必要です。研究する良い例は、型クラスのData.Binaryインスタンスです。Read

例えば

class Binary t where

    -- | Decode a value in the Get monad
    get :: Get t

どんな種類の構文解析をしたいのか、テキストとは言わなかったのですか?バイナリ?-しかし、既存の解析ライブラリを再利用し、タイプのインスタンスを作成するのと同じくらい簡単かもしれません。

または、パーサーコンビネーターを使用して、さまざまなオプションから選択することもできます。

于 2012-06-21T12:53:49.413 に答える
2

私が理解しているように、構成ファイルはパーサーがさらに進む方法を指定します。この非常に単純な例はCSVで、最初の行が後続のすべての行のフィールド数を決定します。すべてのフィールドが文字列フィールドであると仮定すると、私の変形は、最初の行を次の行を解析するパーサーに解析することです。

csvHeader :: Parser (Parser [String])

の結果csvHeaderは、残りのすべての行に適用されるパーサーです。

csvHeader >>= many

あなたの場合、実際の結果も多くのタイプの1つになる可能性があります。これを行う最も簡単な方法は、考えられる結果を伴うADTを用意することです。

data CsvField = Coordinate Float Float | Person Name

より高度で、より安全で、より柔軟なソリューションは、データの観点から考えるのではなく、データに対して実行できる操作の観点から考えることです。整数または浮動小数点数のいずれかの数値を含む構成ファイルを解析しているとします。いずれの場合も、番号を画面に印刷します。したがって、実際の数値を返す代わりに、次の操作を返します。

config :: Parser (IO ())

最後に、型変数を保持しながらフィールドに含まれているものに関する情報を保持したい場合は、実存的になる必要があります。

data Field = forall a. Field a (a -> a)

これで、適用する関数とともに値を返す限り、任意のものを解析できます。

config :: Parser Field
于 2012-06-21T13:33:54.740 に答える
1

あなたの入力例を使用して:

barType: Int
barValue: 2

barTypeの解釈方法を説明しているため、barValue両方の行を同時に解析するか、解析中に状態を維持する (Stateまたはを使用StateT) 必要があります。どちらの場合も、基本的に型のパターン マッチが必要です。たとえば、Data.Binary.Get解析に使用していた場合、次のようになります。

data Bar = IntBar Int | DoubleBar Double

parseBar :: String -> Get Bar
parseBar t = case t of 
  "Int" -> liftM IntBar $ get
  "Double" -> liftM DoubleBar $ get

ポイントは、どのようにスライスしても、パーサーはそれらすべてを考慮する必要があるため、事前に作業するタイプを知る必要があるということです。これは、可能性のあるものすべてを表す型クラスBarは、おそらく進むべき道ではないことも意味します。おそらく、Bar代わりに単純な ADT が必要です。

于 2012-06-21T17:39:37.157 に答える