このHaskellコードでドット演算子が何をしているのかを理解しようとしています:
sumEuler = sum . (map euler) . mkList
ソースコード全体を以下に示します。
ドット演算子は、2つの関数sum
とmap euler
の結果とmkList
の結果を入力として取ります。
しかし、sum
は関数ではなく、関数の引数ですよね?ここで何が起こっているのでしょうか?
また、(map euler)
は何をしていますか?
mkList :: Int -> [Int]
mkList n = [1..n-1]
euler :: Int -> Int
euler n = length (filter (relprime n) (mkList n))
sumEuler :: Int -> Int
sumEuler = sum . (map euler) . mkList
簡単に言えば、_.
_は数学のように関数合成です:
_f (g x) = (f . g) x
_
あなたの場合、新しい関数sumEuler
を作成しています。これも次のように定義できます。
_sumEuler x = sum (map euler (mkList x))
_
この例のスタイルは「ポイントフリー」スタイルと呼ばれます。関数の引数は省略されています。これにより、多くの場合、コードがより明確になります。 (初めて見たときは理解しにくいかもしれませんが、しばらくすると慣れるでしょう。Haskellの一般的なイディオムです。)
それでも混乱している場合は、_.
_をUNIXパイプのようなものに関連付けると役立つ場合があります。 f
の出力がg
の入力になり、その出力がh
の入力になる場合、コマンドラインに_f < x | g | h
_のように記述します。 Haskellでは、_.
_はUNIXの_|
_と同じように機能しますが、「後方」-_h . g . f $ x
_です。たとえば、リストを処理するときに、この表記法が非常に役立つことがわかります。 map (\x -> x * 2 + 10) [1..10]
のような扱いにくい構成の代わりに、単に_(+10) . (*2) <$> [1..10]
_と書くことができます。 (そして、その関数を単一の値にのみ適用したい場合、それは_(+10) . (*2) $ 10
_です。一貫しています!)
Haskell wikiには、さらに詳細な記事があります。 http://www.haskell.org/haskellwiki/Pointfree
。演算子は関数を構成します。例えば、
a . b
aおよびbは関数です新しい関数引数に対してbを実行し、それらの結果に対してaを実行します。あなたのコード
sumEuler = sum . (map euler) . mkList
以下とまったく同じです。
sumEuler myArgument = sum (map euler (mkList myArgument))
しかし、うまくいけば読みやすくなります。 map eulerの周りに括弧があるのは、3つの関数が構成されていることを明確にするためです:sum 、map eulerおよびmkList -map eulerは単一の関数です。
sum
はHaskell Preludeの関数であり、sumEuler
の引数ではありません。タイプがあります
Num a => [a] -> a
関数構成演算子.
にはタイプがあります
(b -> c) -> (a -> b) -> a -> c
だから私たちは
sum :: Num a => [a] -> a
map :: (a -> b) -> [a] -> [b]
euler :: Int -> Int
mkList :: Int -> [Int]
(map euler) :: [Int] -> [Int]
(map euler) . mkList :: Int -> [Int]
sum . (map euler) . mkList :: Int -> Int
Int
はNum
のインスタンスであることに注意してください。
。演算子は関数の構成に使用されます。数学と同じように、関数f(x) and g(x) f。gがf(g(x))になります。
mapは、リストに関数を適用する組み込み関数です。関数を括弧で囲むことにより、関数は引数として扱われます。この用語は currying です。調べてみてください。
これは、2つの引数を持つ関数を取り、引数オイラーを適用することです。 (マップオイラー)そうですか?結果は新しい関数になり、引数を1つだけ取ります。
合計(マップオイラー)。 mkListは、基本的にすべてをまとめた派手な方法です。私のHaskellは少し錆びていますが、最後の機能を自分で組み立てることができますか?
ドット演算子は、左側の関数(sum
)を右側の関数の出力に適用します。あなたの場合、あなたはいくつかの関数をつなぎ合わせています-mkList
の結果を(map euler)
、そしてその結果をsum
に渡します。 このサイト には、いくつかの概念の優れた紹介があります。
Haskellのドット演算子
このHaskellコードでドット演算子が何をしているのかを理解しようとしています:
sumEuler = sum . (map euler) . mkList
ドットなしの同等のコード、それはただ
sumEuler = \x -> sum ((map euler) (mkList x))
またはラムダなし
sumEuler x = sum ((map euler) (mkList x))
ドット(。)は関数の構成を示すためです。
まず、euler
のmap
への部分適用を単純化しましょう。
map_euler = map euler
sumEuler = sum . map_euler . mkList
これでドットができました。これらのドットは何を示していますか?
ソース から:
(.) :: (b -> c) -> (a -> b) -> a -> c (.) f g = \x -> f (g x)
したがって、(.)
は compose演算子 です。
数学では、関数の構成、f(x)およびg(x)、つまりf(g(x))を次のように記述できます。
(f∘g)(x)
「fはgで構成されています」と読むことができます。
Haskellでは、f∘g、またはfをgで構成すると、次のように記述できます。
f . g
合成は結合的です。つまり、合成演算子で記述されたf(g(h(x)))は、あいまいさなしに括弧を省略できます。
つまり、(f∘g)∘hはf∘(g∘h)と同等なので、単純にf∘g∘hと書くことができます。
以前の単純化に戻りましょう、これ:
sumEuler = sum . map_euler . mkList
単にsumEuler
がこれらの関数の適用されていない構成であることを意味します:
sumEuler = \x -> sum (map_euler (mkList x))