57

通常の関数合成は次のタイプです

(.) :: (b -> c) -> (a -> b) -> a -> c

これは次のようなタイプに一般化する必要があると思います。

(.) :: (c -> d) -> (a -> b -> c) -> a -> b -> d

具体的な例:二乗の差の計算。書くことはできますがdiffsq a b = (a - b) ^ 2、を作曲して、のようなものを書くことができるはずだと思います。(-)(^2)diffsq = (^2) . (-)

もちろんできません。私にできることの1つは、に2つの引数の代わりにタプルを使用して、(-)それをで変換することuncurryですが、これは同じではありません。

私がやりたいことをすることは可能ですか?そうでない場合、それが可能であると私に思わせる私は何を誤解していますか?


注:これは事実上ここですでに尋ねられていますが、答え(私は存在しているに違いないと思います)は与えられていません。

4

6 に答える 6

53

これのための私の好ましい実装は

fmap . fmap :: (Functor f, Functor f1) => (a -> b) -> f (f1 a) -> f (f1 b)

覚えやすいからといって。

fとf1をそれぞれにインスタンス化する(->) c(->) d、次のタイプが得られます。

(a -> b) -> (c -> d -> a) -> c -> d -> b

のタイプです

(.) . (.) ::  (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c

しかし、バージョンをガタガタ鳴らすのは少し簡単で、fmap . fmap他のファンクターに一般化されます。

時々これは書かれていますが、より多くの引数を許可するためにもっと簡単に拡張できるのでfmap fmap fmap書かれています。fmap . fmap

fmap . fmap . fmap 
:: (Functor f, Functor g, Functor h) => (a -> b) -> f (g (h a)) -> f (g (h b))

fmap . fmap . fmap . fmap 
:: (Functor f, Functor g, Functor h, Functor i) => (a -> b) -> f (g (h (i a))) -> f (g (h (i b))

一般fmapに、それ自体で構成されたn回は、 fmap nレベルの深さまで使用できます。

また、関数はを形成するため、これはn個の引数Functorの配管を提供します。

詳細については、ConalElliottのSemanticEditorCombinatorsを参照してください。

于 2011-04-28T17:08:35.090 に答える
42

a -> b -> c誤解は、型の関数を戻り型を持つ2つの引数の関数と考えるのcに対し、関数型は右に関連付けられるため、実際には戻り型を持つ1つの引数の関数であるということですb -> c(つまり、これは。と同じa -> (b -> c)です。標準の関数合成演算子を使用できなくなります。

理由を確認するには、(.)演算子型の(y -> z) -> (x -> y) -> (x -> z)演算子を2つの関数にg :: c -> d適用してみてくださいf :: a -> (b -> c)。これは、と統合する必要があることを意味ycますb -> c。これはあまり意味がありません。yと関数の両方がどのようcに返されるのcでしょうか?それは無限のタイプでなければなりません。したがって、これは機能しません。

標準の合成演算子を使用できないからといって、独自の合成演算子を定義することを妨げることはありません。

 compose2 :: (c -> d) -> (a -> b -> c) -> a -> b -> d
 compose2 g f x y = g (f x y)

 diffsq = (^2) `compose2` (-)

通常、この場合はポイントフリースタイルの使用を避けて、

 diffsq a b = (a-b)^2
于 2011-04-28T15:38:51.587 に答える
24

これを行う標準ライブラリ関数はわかりませんが、それを実現するポイントフリーパターンは、合成関数を作成することです。

(.) . (.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c
于 2011-04-28T15:56:34.103 に答える
8

これをコメントで書くつもりでしたが、少し長く、マイティバイトとハンマーの両方から引用しています。

.*forcompose2.**forなどの演算子を中心に標準化することをお勧めしcompose3ます。mightybyteの定義を使用する:

(.*) :: (c -> d) -> (a -> b -> c) -> (a -> b -> d)
(.*) = (.) . (.)

(.**) :: (d -> e) -> (a -> b -> c -> d) -> (a -> b -> c -> e)
(.**) = (.) . (.*)

diffsq :: (Num a) => a -> a -> a
diffsq = (^2) .* (-)

modminus :: (Integral a) => a -> a -> a -> a
modminus n = (`mod` n) .* (-)

diffsqmod :: (Integral a) => a -> a -> a -> a
diffsqmod = (^2) .** modminus

はい、modminusそしてdiffsqmod非常にランダムで価値のない機能ですが、それらは迅速であり、要点を示しています。別の合成関数で合成することにより、次のレベルを定義するのがいかに簡単であるかに注目してください(fmapEdwardが言及した連鎖と同様)。

(.***) = (.) . (.**)

実際には、compose12上から順に、演算子ではなく関数名を記述する方が短くなります。

f .*********** g
f `compose12` g

アスタリスクを数えるのは面倒ですが、4または5でコンベンションを止めたいと思うかもしれません。


[編集]もう1つのランダムなアイデアは、 compose2、 compose3、compose4、compose5、compose6に.:使用でき、ドットの数(最初のドットの後)にドリルダウンする引数の数を視覚的に示します。でも星の方が好きだと思います。.:..::.::..:::

于 2011-04-28T17:41:35.133 に答える
6

マックスがコメントで指摘したように:

diffsq = ((^ 2) .) . (-)

f . gに1つの引数を適用しg、その結果をに渡すと考えることができますf(f .) . gに2つの引数を適用しg、結果をに渡しfます。((f .) .) . gに3つの引数を適用gします。

\f g -> (f .) . g :: (c -> d) -> (a -> b -> c) -> a -> b -> d

いくつかの関数(左側にあるf :: c -> d部分適用)を使用して合成演算子を左セクションにすると、次のようになります。f

(f .) :: (b -> c) -> b -> d

したがって、からの関数を期待するこの新しい関数がありますがb -> cgは、、a -> b -> cまたは同等にa -> (b -> c)です。a必要なものを入手する前に、申請する必要があります。さて、もう一度繰り返しましょう:

((f .) .) :: (a -> b -> c) -> a -> b -> d
于 2016-03-23T03:27:40.563 に答える
3

これがあなたが望むものを達成するためのエレガントな方法だと私が思うものです。型クラスはFunctor、関数をコンテナに「プッシュ」する方法を提供するため、を使用して各要素に関数を適用できますfmap。関数は、各要素がの要素によってインデックス付けされたsのa -> bコンテナと考えることができます。したがって、このインスタンスを作成するのは自然なことです。ba

instance Functor ((->) a) where
  fmap f g = f . g

(適切なライブラリを使用することでそれを取得できると思いますが、importどちらかは思い出せません。)

fこれで、 withの通常の構成gは自明fmapです:

o1 :: (c -> d) -> (b -> c) -> (b -> d)
f `o1` g = fmap f g

タイプの関数は、タイプa -> b -> cの要素のコンテナのコンテナですcfしたがって、関数を2回プッシュダウンする必要があります。どうぞ:

o2 :: (c -> d) -> (a -> (b -> c)) -> a -> (b -> d)
f `o2` g = fmap (fmap f) g

実際には、必要がない、o1またはo2、ただfmapfmapまた、場所を忘れたライブラリが見つかった場合は、追加のコードを記述せずにそのまま使用できることがわかります。

于 2011-04-28T17:18:06.930 に答える