関数型プログラミングにおけるバナナの分割と融合とは何ですか?


22

これらの用語は私の大学のコースで言及されました。クイックグーグルで大学の論文をいくつか指摘されましたが、簡単な説明を探しています。


@jozefg:投稿へのリンクをありがとう。それについての1つの質問。「この意味での代数はオブジェクトCとマップFC→Cのペアです」という文では、Cは実際にはオブジェクト、またはむしろカテゴリであると想定されていますか?言い換えれば、Fがカテゴリ内のファンクターを示しているかどうか、そしてFがオブジェクトからそれ自体への特定の矢印である場合、F代数がそのファンクターによって誘導された代数であるかどうかはわかりません。
ジョルジオ

Cは、あるカテゴリのオブジェクト(たとえばCC)でFあり、ファンクタであるCC -> CCため、それCC自体にマップされます。これF CC -> CCは、カテゴリ内の通常の矢印CCです。だからF代数はオブジェクトでありC : CC、矢印F C -> CCC
ダニエルGratzer

回答:


4

すでに2つの答えが提供されていますが、ここでは「バナナの分裂」については説明されていないと思います。

実際、「バナナ、レンズ、エンベロープ、有刺鉄線を使用した関数型プログラミング、Erik Meijer Maarten Fokkinga、Ross Paterson、1991」で定義されています。その記事は、Squiggolを多用しているため、読みにくいです(私にとって)。ただし、「フォールドの普遍性と表現力に関するチュートリアル、Graham Hutton、1999」には、解析しやすい定義が含まれています。

foldを使用してタプルを生成する簡単な最初の例として、数値リストの合計長さを計算する関数sumlengthを考えます。

sumlength :: [Int] → (Int,Int)
sumlength xs = (sum xs, length xs)

前に示したfoldを使用した関数sumlengthの定義の単純な組み合わせにより、関数sumlengthは、数値のリストから数値のペアを生成するfoldの単一のアプリケーションとして再定義できます。

sumlength = fold (λn (x, y) → (n + x, 1 + y)) (0, 0)

この定義は、2つの別個のトラバーサルではなく、引数リストに対して1つのトラバーサルのみを行うため、元の定義よりも効率的です。この例から一般化、のアプリケーションの任意のペア同じリストには、常に、単一のアプリケーション与えるために組み合わせることができる折り畳みのいわゆる「バナナスプリット」プロパティにアピールすることで、ペアを生成(マイヤー、1992) 。このプロパティの奇妙な名前は、折り演算子がバナナに似た括弧(| |)を使用して記述され、ペア演算子が分割と呼ばれることもあるという事実に由来しています。したがって、それらの組み合わせはバナナスプリットと呼ばれます!


19

つまり、これは実際にはMeijerと「バナナ、レンズ、エンベロープ、および有刺鉄線を使用した関数型プログラミング」と呼ばれる他のいくつかの論文を参照しています。基本的な考え方は、

 data List = Cons Int List | Nil

そして、再帰を型変数に分解することができます

 data ListF a = Cons Int a | Nil

Fこれを追加した理由は、これが今ではファンクターだからです!また、リストを模倣することもできますが、ひねりを加えて:リストを作成するには、リストタイプをネストする必要があります

type ThreeList = ListF (ListF (ListF Void)))

元のリストを復元するには、これを無限にネストし続ける必要があります。それは私たちにタイプ与えるListFF場所を

  ListF ListFF == ListFF

これを行うには、「固定小数点タイプ」を定義します

  data Fix f = Fix {unfix :: f (Fix f)}
  type ListFF = Fix ListF

演習として、これが上記の式を満たしていることを確認する必要があります。これで、最終的にバナナ(異形)を定義できます。

  type ListAlg a = ListF a -> a

ListAlgsは「リスト代数」のタイプであり、特定の関数を定義できます

  cata :: ListAlg a -> ListFF -> a
  cata f = f . fmap (cata f) . unfix

さらにもっと

  cata :: ListAlg a -> ListFF -> a
  cata :: (Either () (Int, a) -> a) -> ListFF -> a
  cata :: (() -> a) -> ((Int, a) -> a) -> ListFF -> a
  cata :: a -> (Int -> a -> a) -> ListFF -> a
  cata :: (Int -> a -> a) -> a -> [Int] -> a

見覚えがあります?cata右折りとまったく同じです!

本当に興味深いのは、この「ファンクターの固定点」で定義されたタイプにはリストだけでなく、リストを超えてこれを実行できることcataと、それらすべてを受け入れるためにタイプシグネチャを緩和するだけです。

  cata :: (f a -> a) -> Fix f -> a

これは実際に私が書いたカテゴリー理論からインスピレーションを受けていますが、これはHaskell側の要です。


2
バナナは、元の紙がカタ
jk

7

jozefgが答えを提供しましたが、質問に答えたかどうかはわかりません。「融合の法則」は次の論文で説明されています。

折り畳みの普遍性と表現力に関するチュートリアル、GRAHAM HUTTON、1999

基本的に、ある条件下では、関数の構成を結合(「融合」)して単一の折り畳みに折り畳むことができると言われているので、基本的に

h・fold gw = fold fv

この平等の条件は

hw = v
h(gxy)= fx(hy)

「バナナスプリット」または「バナナスプリット法」は、記事からのものです

バナナ、レンズ、エンベロープ、有刺鉄線を使用した関数型プログラミング、Erik Meijer Maarten Fokkinga、Ross Paterson、1991

残念ながら、Bird-Meertensの形式を使用しているため、この記事を解読するのは非常に困難です。「バナナ分割法」を理解している限り、同じ引数で2つのフォールドが動作している場合、それらを1つのフォールドにマージできると述べています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.