あなたが例を取るなら
map :: (a -> b) -> [a] -> [b]
これは、マップが2つの引数を取ることを意味します
- タイプaからタイプbへの関数
- タイプaのリスト
そしてそれは戻ります
- bのリスト
すでにここでパターンを見ることができますが、「a」と「b」を置き換えるとより明確になります
したがって、このタイプの定義は次のようになります。
map :: (String -> Int) -> [String] -> [Int]
これで、文字列を受け取ってIntを返す関数と、文字列のリストを受け取ってIntのリストを返す関数になりました。
文字列を受け取って戻り、Intがである関数を考えてみましょうread
。read
指定した文字列を使用して、別の文字列に変換します。ここではIntコンテキストに配置しているため、文字列をintに変換します
map read ["1", "2", "112", 333"]
これにより、
[1, 2, 112, 333]
map
関数read
を受け取り、それをリストのすべての要素にマップ(適用)するからです。あなたはそれがあなたのためにそれを世話するので、、またはread
のような議論を言う必要はありません。read "1"
read n
map
そして、それが本当にすべてです。関数の型は、どの型を取り、どの型を返すかだけを示します。もちろん、カリー化もありますが、わざとかどうかにかかわらず、後でそれを取得します!これは基本的に、関数が多くの引数をとらず、1つだけをとることを意味します。関数を実行するとします+
。を評価する1+2
と、に追加された別の数値を受け取る関数が返されます。1
ここに別の数値があるため2
、それを使用します。あなたはそれをとして評価し、それ(1+)
を渡すことができたかもしれません、おそらくしばらくして番号を追加しました。のインフィックス構文がない場合は、より明確になります+
。書かれている可能性が(+) 1 2
あるので、最初にこのステートメントは次のようになります(+) 1
、これは型です(簡略化されています!ここではNum型クラスを紹介しません)Int -> Int
。したがって(+) 1
、(または(1+)
そのことについては)値を適用できる別の関数であり、その時点で結果は3に計算できるようになります。
これが実際にどのように見えるかを示します:((Num a) =>
混乱する場合は、ここの部分を無視してください。これは後で詳しく説明する概念です。必要に応じて、a
ここのタイプを置き換えてInt
、部分を完全に無視して(Num a) =>
ください。)
(+) :: (Num a) => a -> a -> a
(+2) :: (Num a) => a -> a
(1+) :: (Num a) => a -> a
(1+2) :: (Num a) => a
Prelude> (+2) 5
7
Prelude> map (+3) [1,2,3]
[4,5,6]
そして2番目の質問に:あなたは推論された型をまったく定義しません。コンパイラー/インタープリターが明示的に名前を付けずに型自体を「推論」(読み取り:計算)するため、これらは「推論」と呼ばれます。
違いについてfoldX
:それらはすべてまったく同じことをします:リストを単一の値に減らします。関数間の違いは、単に内部定義です。foldl
リストを左から折り、foldr
右に折ります。
したがって、リストを要約すると、これらすべてを次のように使用できます...
foldl1 (+) [1,2,3] == 6
foldr (+) 0 [1,2,3] == 6
foldl (+) 0 [1,2,3] == 6
foldl1を除いて、折りたたむ関数、開始値(アキュムレータ)、および折りたたむリストを指定します。fold1
それはそれ自身のアキュムレータを持っています、あなたはそれにあなた自身のものを与えません。
実際には、スタックオーバーフロー(tee、hee)によってクラッシュすることなく、BIGリストに適しているfoldr
ため、を使用する方が適切です。fold
そして、このルールの例外はfoldl'
( "'"!に注意してください!)Data.Map
-これは厳密な折り畳みであり、大きなリストにも適しています。
関数に自分で型を指定したい場合(作成した場合)、次の例を検討してください。
double :: Int -> Int
double n = 2 * n
または面倒な方法で
double :: Int -> Int
double = (*2)
両方の例の最初の行(double :: Int -> Int
)は、探しているものです。これは、コンパイラーまたはインタープリターに関数を識別させる方法です。その場合は省略でき、コンパイラー/インタープリターはそれらを検出します(場合によっては、最初に考えたよりも優れた方法で)。