任意の数の引数(すべて同じ型)を受け取り、それらを使用して何らかの結果を返す関数が必要です。引数のリストは、私の特定のケースでは実行不可能です。
Haskellライブラリを調べたところ、関数printf
(モジュールText.Printf
から)が同様のトリックを使用していることがわかりました。残念ながら、ソースを見てもその魔法は理解できませんでした。
誰かがこれを達成する方法を説明できますか、または少なくともいくつかのウェブページ/紙/私がこれについての良い説明を見つけることができる場所は何ですか?
動機:
これが必要な理由は本当に簡単です。学校(コンピュータサイエンスクラス)では、数式を「記録」し、それを文字列として表現できるモジュール(独自のデータ型のNum/Real/etcなどのインスタンスを書き込む)を実行し、実行する必要があります。さまざまな操作。
このデータ型には、変数の特別なコンストラクターが含まれています。これは、値または指定された関数によって何でも置き換えられます。目標の1つは、いくつかの変数(タイプ(Char,Rational)
のペア)を持つ式を受け取り、式の結果を計算する関数を記述することです。関数の目標を最もよく表す方法を検討する必要があります。 (私の考え:関数は、関数で定義されている変数と同じ数の引数を取る別の関数を返します-不可能のようです)。
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
多くの人が可変関数の作成方法を教えてくれていますが、この場合は、タイプ[(Char、Rational)]のリストを使用する方が実際には良いと思います。
KennyTMの答えは素晴らしいです。以下は、よりわかりやすくするためのsumOf 1 4 7 10 :: Integer
のexecプロセスの例です。
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
可変関数に関するWikiの記事で、 この記事 が参照されました。これはprintfが行うことだと思いますが、私もそれを理解していません。とにかく、これは確かに過剰です。特に、引数がすべて同じ型であるためです。それらすべてを1つのリストに入れるだけです。これがリストに適しています-同じタイプの任意の数の物。細かいですが、それほど美しくはありませんが、完全な多変量関数ほど醜くはありません。
Delnanが参照した記事からリンクされている 例 を調べました。少し見つめた後、ようやく何が起こっているのかを理解できたと思います。
それはこのタイプのクラスから始まります:
class BuildList a r | r-> a where
build' :: [a] -> a -> r
パイプ(|)の後のそのビットは、機能の依存関係です。 a
によって表される型は、r
によって表される型によって決定できると述べています。つまり、BuildList
(戻り値の型)が同じでr
が異なるa
タイプクラスの2つのインスタンスを定義することはできません。
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)
は、タイプBool -> [Bool]
の関数を返すことが期待されています。これにより、BuildList
タイプクラスの2番目のインスタンスにバインドされます。
instance BuildList a r => BuildList a (a->r) where
build' l x y = build'(x:l) y
この呼び出しでは、l = []
およびx = True
およびy = False
であるため、定義はbuild' [True] False :: [Bool]
に拡張されます。そのシグネチャはbuild'
の最初のインスタンスにバインドされ、そこからどこに行くのかはかなり明白です。