77

任意の数の引数(すべて同じタイプ)を取り、それらを使用して何かを実行し、その後結果を返す関数が必要です。私の特定のケースでは、引数のリストは実用的ではありません。

haskell libsを調べてみると、printf(モジュールからのText.Printf)関数が同様のトリックを使用していることがわかりました。残念ながら、ソースを見てもその魔法は理解できませんでした。

誰かがこれを達成する方法、または少なくともいくつかのウェブページ/紙/私がこれについての良い説明を見つけることができるところなら何でも説明できますか?

動機:

私がこれを必要とする理由は本当に非常に単純です。学校(コンピュータサイエンスの授業)では、数式を「記録」して文字列として表現し(独自のデータ型のNum / Realなどのインスタンスを書き込むことにより)、実行できるモジュールを作成する必要があります。その上でさまざまな操作。

このデータ型には、変数の特別なコンストラクターが含まれています。このコンストラクターは、値など、指定された関数で置き換えることができます。目標の1つは、いくつかの変数(型のペア(Char,Rational))を持つそのような式を取り、式の結果を計算する関数を作成することです。関数の目的を最もよく表現する方法を検討する必要があります。(私の考え:関数は、関数で定義されている変数とまったく同じ数の引数を取る別の関数を返します-不可能のようです)。

4

5 に答える 5

110

の重要なポイントはprintf、文字列または関数のいずれかを返す機能です。http://www.haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/src/Text-Printf.htmlからコピー、

printf :: (PrintfType r) => String -> r
printf fmts = spr fmts []

class PrintfType t where
    spr :: String -> [UPrintf] -> t

instance (IsChar c) => PrintfType [c] where
    spr fmts args = map fromChar (uprintf fmts (reverse args))

instance (PrintfArg a, PrintfType r) => PrintfType (a -> r) where
    spr fmts args = \a -> spr fmts (toUPrintf a : args)

抽出できる基本構造は

variadicFunction :: VariadicReturnClass r => RequiredArgs -> r
variadicFunction reqArgs = variadicImpl reqArgs mempty

class VariadicReturnClass r where
   variadicImpl :: RequiredArgs -> AccumulatingType -> r

instance VariadicReturnClass ActualReturnType where
   variadicImpl reqArgs acc = constructActualResult reqArgs acc

instance (ArgClass a, VariadicReturnClass r) => VariadicReturnClass (a -> r) where
   variadicImpl reqArgs acc = \a -> variadicImpl reqArgs (specialize a `mappend` acc)

例えば:

class SumRes r where 
    sumOf :: Integer -> r

instance SumRes Integer where
    sumOf = id

instance (Integral a, SumRes r) => SumRes (a -> r) where
    sumOf x = sumOf . (x +) . toInteger

次に、使用できます

*Main> sumOf 1 :: Integer
1
*Main> sumOf 1 4 7 10 :: Integer
22
*Main> sumOf 1 4 7 10 0 0  :: Integer
22
*Main> sumOf 1 4 7 10 2 5 8 22 :: Integer
59
于 2010-08-12T12:51:45.627 に答える
10

多くの人が可変引数関数の作成方法を教えていますが、この場合は [(Char,Rational)] 型のリストを使用する方が実際には良いと思います。

于 2010-08-12T13:56:14.203 に答える
10

KennyTM の答えは素晴らしいです。sumOf 1 4 7 10 :: Integer以下は、より分かりやすいように の実行プロセスの例です。

sumOf 1 4 7 10
(( \ x -> ( sumOf . (x +) . toInteger ) 1 ) 4 7 10
((sumOf . (1 + ) . toInteger) 4 ) 7 10
( sumOf 5 ) 7 10
( sumOf . (5 + ) . toInteger ) 7 10
sumOf 12 10
sumOf . (12 + ) . toInteger 10
sumof 22
id 22
22
于 2013-10-04T14:37:09.640 に答える
7

可変引数関数に関する wiki 記事で、この記事が参照されました。これはprintfが行うことだと思いますが、私もそれを理解していません。とにかく、特に引数がすべて同じ型であるため、これは確かにやり過ぎです。それらすべてを 1 つのリストに入れるだけです。それがリストの良いところです - 同じタイプのものの任意の数。これはあまり美しくありませんが、完全な多変量関数よりも醜いことはほとんどありません。

于 2010-08-12T12:05:18.270 に答える
6

デルナンが参照した記事からリンクされた例を見てみました。それを少し見つめた後、私は最終的に何が起こっているのかを理解したと思います:

次の型クラスで始まります。

class BuildList a r  | r-> a where
    build' :: [a] -> a -> r

パイプ (|) の後のビットは機能依存関係です。で表される型は で表されるa型で判別できるということrです。BuildListつまり、型クラスの 2 つのインスタンスを同じr(戻り値の型) で定義することはできませんが、異なるa.

build'関数が実際に使用されている場所に少しジャンプします。

> build True :: [Bool]

build最初のパラメーターとして空のリストを呼び出しているだけなのでbuild'、これは次と同じです。

> build' [] True :: [Bool]

この例でbuild'は、明らかにリストを返しています。BuildList関数の依存関係のため、型クラスのこのインスタンスにのみバインドできます。

instance BuildList a [a] where
    build' l x = reverse$ x:l

かなり簡単です。2 番目の例は、より興味深いものです。の定義を展開すると、次のbuildようになります。

> build' [] True False :: [Bool]

build'この場合の型は何ですか?さて、Haskell の優先順位規則は、上記が次のように書けることを意味します:

> (build' [] True) False :: [Bool]

ここで、2 つのパラメーターを に渡しbuild'、その式の結果を値 'False' を持つパラメーターに適用していることは明らかです。つまり、式は型の関数(build' [] True)を返すことが期待されます。これにより、型クラスの 2 番目のインスタンスにバインドされます。Bool -> [Bool]BuildList

instance BuildList a r => BuildList a (a->r) where
    build' l x y = build'(x:l) y

この呼び出しではl = []、 およびx = Truey = Falseあるため、定義は に展開されbuild' [True] False :: [Bool]ます。その署名は の最初のインスタンスにバインドされ、build'そこからどこへ行くのかは明らかです。

于 2010-08-12T13:44:21.207 に答える