web-dev-qa-db-ja.com

多変量ハスケル関数を作成する方法は?

任意の数の引数(すべて同じ型)を受け取り、それらを使用して何らかの結果を返す関数が必要です。引数のリストは、私の特定のケースでは実行不可能です。

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

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

動機:

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

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

69
fuz

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
101
kennytm

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

9
user370536

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
8
lispc

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

7
user395760

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'の最初のインスタンスにバインドされ、そこからどこに行くのかはかなり明白です。

6
Daniel Pratt