web-dev-qa-db-ja.com

何をしますか(f。)。 gはHaskellの意味ですか?

パターン(f .) . gに従って定義されている関数をたくさん見てきました。例えば:

countWhere = (length .) . filter
duplicate  = (concat .) . replicate
concatMap  = (concat .) . map

これは何を意味するのでしょうか?

49
Aadit M Shah

ドット演算子(つまり、_(.)_)は 関数合成 演算子です。これは次のように定義されています。

_infixr 9 .
(.) :: (b -> c) -> (a -> b) -> a -> c
f . g = \x -> f (g x)
_

ご覧のとおり、タイプ_b -> c_の関数とタイプ_a -> b_の別の関数を取り、タイプ_a -> c_の関数を返します(つまり、最初の関数を2番目の関数の結果に適用します) )。

関数合成演算子は非常に便利です。これにより、ある関数の出力を別の関数の入力にパイプすることができます。たとえば、Haskellで tac プログラムを次のように書くことができます。

_main = interact (\x -> unlines (reverse (lines x)))
_

あまり読みにくい。ただし、関数合成を使用すると、次のように記述できます。

_main = interact (unlines . reverse . lines)
_

ご覧のとおり、関数の合成は非常に便利ですが、どこでも使用できるわけではありません。たとえば、関数合成を使用してfilterの出力をlengthにパイプすることはできません。

_countWhere = length . filter -- this is not allowed
_

これが許可されない理由は、filterのタイプが_(a -> Bool) -> [a] -> [a]_であるためです。 _a -> b_と比較すると、aのタイプは_(a -> Bool)_であり、bのタイプは_[a] -> [a]_であることがわかります。 Haskellはlengthが_b -> c_型(つまり_([a] -> [a]) -> c_)であると想定しているため、これにより型の不一致が発生します。ただし、実際にはタイプ_[a] -> Int_です。

解決策は非常に簡単です。

_countWhere f = length . filter f
_

ただし、その余分なぶら下がりfが気に入らない人もいます。彼らはcountWherepointfree スタイルで次のように書くことを好みます:

_countWhere = (length .) . filter
_

彼らはどうやってこれを手に入れますか?考えてみましょう:

_countWhere f xs = length (filter f xs)

-- But `f x y` is `(f x) y`. Hence:

countWhere f xs = length ((filter f) xs)

-- But `\x -> f (g x)` is `f . g`. Hence:

countWhere f = length . (filter f)

-- But `f . g` is `(f .) g`. Hence:

countWhere f = (length .) (filter f)

-- But `\x -> f (g x)` is `f . g`. Hence:

countWhere = (length .) . filter
_

ご覧のとおり、_(f .) . g_は単に\x y -> f (g x y)です。この概念は実際に繰り返すことができます。

_f . g             --> \x -> f (g x)
(f .) . g         --> \x y -> f (g x y)
((f .) .) . g     --> \x y z -> f (g x y z)
(((f .) .) .) . g --> \w x y z -> f (g w x y z)
_

それはきれいではありませんが、それは仕事を成し遂げます。 2つの関数が与えられた場合、独自の関数合成演算子を作成することもできます。

_f .: g = (f .) . g
f .:: g = ((f .) .) . g
f .::: g = (((f .) .) .) . g
_

_(.:)_演算子を使用すると、代わりに次のようにcountWhereを記述できます。

_countWhere = length .: filter
_

興味深いことに、ポイントフリースタイルで_(.:)_を書くこともできます。

_f .: g = (f .) . g

-- But `f . g` is `(.) f g`. Hence:

f .: g = (.) (f .) g

-- But `\x -> f x` is `f`. Hence:

(f .:) = (.) (f .)

-- But `(f .)` is `((.) f)`. Hence:

(f .:) = (.) ((.) f)

-- But `\x -> f (g x)` is `f . g`. Hence:

(.:) = (.) . (.)
_

同様に、次のようになります。

_(.::)  = (.) . (.) . (.)
(.:::) = (.) . (.) . (.) . (.)
_

ご覧のとおり、_(.:)_、_(.::)_、および_(.:::)_は_(.)_の累乗です(つまり、_(.)_の 反復関数 ) 。数学の数字の場合:

_x ^ 0 = 1
x ^ n = x * x ^ (n - 1)
_

数学の関数についても同様です。

_f .^ 0 = id
f .^ n = f . (f .^ (n - 1))
_

fが_(.)_の場合、次のようになります。

_(.) .^ 1 = (.)
(.) .^ 2 = (.:)
(.) .^ 3 = (.::)
(.) .^ 4 = (.:::)
_

これで、この記事の終わりに近づきました。最後の課題として、次の関数をポイントフリースタイルで記述しましょう。

_mf a b c = filter a (map b c)

mf a b c = filter a ((map b) c)

mf a b = filter a . (map b)

mf a b = (filter a .) (map b)

mf a = (filter a .) . map

mf a = (. map) (filter a .)

mf a = (. map) ((filter a) .)

mf a = (. map) ((.) (filter a))

mf a = ((. map) . (.)) (filter a)

mf = ((. map) . (.)) . filter

mf = (. map) . (.) . filter
_

これを次のようにさらに単純化できます。

_compose f g = (. f) . (.) . g

compose f g = ((. f) . (.)) . g

compose f g = (.) ((. f) . (.)) g

compose f = (.) ((. f) . (.))

compose f = (.) ((. (.)) (. f))

compose f = ((.) . (. (.))) (. f)

compose f = ((.) . (. (.))) (flip (.) f)

compose f = ((.) . (. (.))) ((flip (.)) f)

compose = ((.) . (. (.))) . (flip (.))
_

composeを使用すると、mfを次のように書くことができます。

_mf = compose map filter
_

はい、それは少し醜いですが、それは本当に素晴らしい気が遠くなるような概念でもあります。これで、\x y z -> f x (g y z)の形式の任意の関数を_compose f g_として記述できます。

90
Aadit M Shah

これは好みの問題ですが、そのようなスタイルは不快だと思います。最初にそれが何を意味するのかを説明し、次に私が好む代替案を提案します。

演算子(f . g) x = f (g x)(f ?) x = f ? x?を知っておく必要があります。これから私たちはそれを推測することができます

countWhere p = ((length .) . filter) p
              = (length .) (filter p)
              = length . filter p

そう

countWhere p xs = length (filter p xs)

.:という関数を使用することを好みます

(.:) :: (r -> z) -> (a -> b -> r) -> a -> b -> z
(f .: g) x y = f (g x y)

次にcountWhere = length .: filter。個人的には、これははるかに明確だと思います。

.:Data.Compositionおよびおそらく他の場所でも定義されています。)

12
Tom Ellis