関数構成vs関数アプリケーション
-
29-09-2019 - |
質問
- 誰かが関数の構成の例を挙げますか?
これは関数構成演算子の定義ですか?
(.) :: (b -> c) -> (a -> b) -> a -> c f . g = \x -> f (g x)
これは、2つの関数を取り、関数を返すことを示していますが、誰かが英語で論理を表現したことを覚えています
少年は人間です - >アリは男の子です - >アリは人間です
- このロジックは関数構成に関連していますか?
- 関数アプリケーションと組成の強力な結合の意味は何ですか?また、どちらが他方よりも強力な結合ですか?
助けてください。
ありがとう。
解決
(編集1: 私はあなたの質問のいくつかのコンポーネントを初めて見逃しました。私の答えの一番下を見てください。)
この種の声明について考える方法は、タイプを見ることです。あなたが持っている議論の形式は三段論法と呼ばれます。しかし、私はあなたが何かを忘れていると思います。さまざまな種類の三段論法があり、あなたのものは、私が知る限り、関数の構成に対応していません。一種の三段論法を考えてみましょう。
- それが晴れているなら、私は熱くなります。
- 暑くなったら泳ぎに行きます。
- したがって、 それが晴れているなら、私は泳ぎに行きます。
これはaと呼ばれます 仮説的な三段論法. 。論理用語では、次のように書きます。 s 「それは晴れている」という命題を表してください。 h 「私は熱くなります」という命題を表し、 w 「私は水泳に行きます」という命題を表します。書き込み α → β にとって "α 示す β"、そして「したがって」のために書いて、上記を次のように翻訳できます。
- s → h
- h → w
- ∴ s → w
もちろん、これは交換すると機能します s, h, 、 と w 任意の任意の場合 α, β, 、 と γ. 。今、これはおなじみに見えるはずです。意味の矢印→を変化させると、関数矢印に ->
, 、これはなります
a -> b
b -> c
- ∴
a -> c
そして、見よ、組成演算子のタイプの3つのコンポーネントがあります!これを論理的な三段論法と考えるには、以下を考慮することができます。
- タイプの値がある場合
a
, 、タイプの値を作成できますb
. - タイプの値がある場合
b
, 、タイプの値を作成できますc
. - したがって、 タイプの値がある場合
a
, 、タイプの値を作成できますc
.
これは理にかなっているはずです:in f . g
, 、関数の存在 g :: a -> b
前提1は真実であることを伝えます f :: b -> c
前提2が真実であることを伝えます。したがって、あなたは関数のために最終的なステートメントを締めくくることができます f . g :: a -> c
証人です。
あなたの三段論法が何に翻訳されるかは完全にはわかりません。それはほとんどの例です モーダスポーネン, 、しかし、そうではありません。 Modus ponensの引数は、次の形式を取ります。
- 雨が降っている場合は、濡れます。
- 雨が降っている。
- したがって、 私は濡れます。
書き込み r 「雨が降っている」と w 「私は濡れます」のために、これは私たちに論理的な形を与えます
- r → w
- r
- ∴ w
意味矢印を関数矢印に置き換えると、次のことがわかります。
a -> b
a
- ∴
b
そして、これは単に関数アプリケーションです。 ($) :: (a -> b) -> a -> b
. 。これを論理的な議論と考えたい場合、それは形のかもしれません
- タイプの値がある場合
a
, 、タイプの値を作成できますb
. - タイプの値があります
a
. - したがって、 タイプの値を作成できます
b
.
ここでは、表現を考えてください f x
. 。関数 f :: a -> b
命題1の真実の証人です。値 x :: a
命題2の真実の証人です。したがって、結果はタイプになります b
, 、結論を証明します。それはまさに私たちが証明から見つけたものです。
さて、あなたの元の三段論法は次の形式を取ります:
- すべての男の子は人間です。
- アリは男の子です。
- したがって、 アリは人間です。
これをシンボルに翻訳しましょう。 BX それを示します バツ 男の子です。 HX それを示します バツ 人間です。 a アリを表します。と∀バツ. φ それは言う φ のすべての値に当てはまります バツ. 。それから私たちは持っています
- ∀バツ. BX → HX
- ba
- ∴ ハ
これはほぼモデアですが、すべてをインスタンス化する必要があります。論理的に有効ですが、タイプシステムレベルでそれを解釈する方法がわかりません。誰かが助けたいなら、私はすべて耳です。推測の1つは、ランク2タイプのようなものです (forall x. B x -> H x) -> B a -> H a
, 、しかし、私はそれが間違っていると確信しています。別の推測は、ようなよりシンプルなタイプです (B x -> H x) -> B Int -> H Int
, 、 どこ Int
アリの略ですが、繰り返しますが、私はそれが間違っていると確信しています。繰り返しますが、あなたが知っているなら、私にも知らせてください!
そして最後のメモ。このように物事を見る - 証明とプログラムの間のつながりに耐える - は、最終的にの深い魔法につながります カレー - ハワード同型, 、しかし、それはより高度なトピックです。 (本当にかっこいい!)
編集1: また、関数構成の例を求めました。これが一例です。私が人々のミドルネームのリストを持っているとします。すべてのミドルイニシャルのリストを作成する必要がありますが、そのためには、まずすべての存在しないミドルネームを除外する必要があります。ミドルネームがnullであるすべての人を除外するのは簡単です。私たちはただ 含む ミドルネームがあるすべての人 いいえ null filter (\mn -> not $ null mn) middleNames
. 。同様に、私たちは誰かの真ん中のイニシャルで簡単に取得できます head
, 、そして、単に必要です map head filteredMiddleNames
リストを取得するため。つまり、次のコードがあります。
allMiddleInitials :: [Char]
allMiddleInitials = map head $ filter (\mn -> not $ null mn) middleNames
しかし、これは刺激的に具体的です。私たちは本当に中心的な生成関数が欲しいです。それでは、これを1つに変更しましょう。
getMiddleInitials :: [String] -> [Char]
getMiddleInitials middleNames = map head $ filter (\mn -> not $ null mn) middleNames
さて、何か面白いことを見てみましょう。関数 map
タイプがあります (a -> b) -> [a] -> [b]
, 、 それ以来 head
タイプがあります [a] -> a
, map head
タイプがあります [[a]] -> [a]
. 。同様に、 filter
タイプがあります (a -> Bool) -> [a] -> [a]
, 、 など filter (\mn -> not $ null mn)
タイプがあります [a] -> [a]
. 。したがって、パラメーターを取り除くことができ、代わりに書くことができます
-- The type is also more general
getFirstElements :: [[a]] -> [a]
getFirstElements = map head . filter (not . null)
そして、あなたは私たちには構成のボーナスインスタンスがあることがわかります: not
タイプがあります Bool -> Bool
, 、 と null
タイプがあります [a] -> Bool
, 、 それで not . null
タイプがあります [a] -> Bool
: :最初に指定されたリストが空であるかどうかをチェックし、次にそれを返すかどうかを返します そうではありません. 。ちなみに、この変換は関数を変えました ポイントフリースタイル;つまり、結果の関数には明示的な変数がありません。
また、「強力なバインディング」についても尋ねました。私があなたが言及していると思うのは、 .
と $
オペレーター(およびおそらく機能アプリケーション)。 Haskellでは、算術と同様に、特定の演算子は他の演算子よりも優先され、したがってよりしっかりと結合します。たとえば、式で 1 + 2 * 3
, 、これは解析されます 1 + (2 * 3)
. 。これは、Haskellでは、次の宣言が有効であるためです。
infixl 6 +
infixl 7 *
数(優先度レベル)が高いほど、その演算子が呼び出されるのは早く、したがってオペレーターがバインドします。関数アプリケーションは効果的に無限の優先順位を持っているため、最も緊密に結合します:式 f x % g y
ASとして解析します (f x) % (g y)
任意のオペレーターの場合 %
. 。 .
(構成)と $
(アプリケーション)オペレーターには、次の固定宣言があります。
infixr 9 .
infixr 0 $
優先レベルはゼロから9の範囲であるため、これが言っているのは .
オペレーターがバインドします もっと 他のどの(関数アプリケーションを除く)よりも厳密に $
バインド 以下 しっかりと。したがって、式 f . g $ h
ASとして解析します (f . g) $ h
;そして実際、ほとんどのオペレーターにとって、 f . g % h
そうなるでしょう (f . g) % h
と f % g $ h
そうなるでしょう f % (g $ h)
. 。 (唯一の例外は、他のゼロまたは9つの優先順位演算子がまれであることです。)