4

「バインド」操作のみを使用して、 Scheme(またはPython)mapおよび関数と同等のリストモナドを実行する方法を知っています。filter

説明するScalaは次のとおりです。

scala> // map
scala> List(1,2,3,4,5,6).flatMap {x => List(x * x)}                        
res20: List[Int] = List(1, 4, 9, 16, 25, 36)

scala> // filter    
scala> List(1,2,3,4,5,6).flatMap {x => if (x % 2 == 0) List() else List(x)}
res21: List[Int] = List(1, 3, 5)

そしてHaskellでも同じことが言えます:

Prelude> -- map
Prelude> [1, 2, 3, 4, 5, 6] >>= (\x -> [x * x])
[1,4,9,16,25,36]

Prelude> -- filter
Prelude> [1, 2, 3, 4, 5, 6] >>= (\x -> if (mod x 2 == 0) then [] else [x])
[1,3,5]

SchemeとPythonには、reduceとでグループ化されることが多い関数もmapありfilterます。このreduce関数は、提供されたバイナリ関数を使用してリストの最初の2つの要素を結合し、次にその結果を次の要素に結合し、以下同様に続きます。値のリストの合計または積を計算するための一般的な使用法。説明するPythonを次に示します。

>>> reduce(lambda x, y: x + y, [1,2,3,4,5,6])
21
>>> (((((1+2)+3)+4)+5)+6)
21

reduceリストモナドのバインド操作だけを使用してこれと同等の方法はありますか?bindがそれ自体でこれを実行できない場合、この操作を実行するための最も「モナディック」な方法は何ですか?

可能であれば、do回答するときは、構文糖衣の使用を制限/回避してください(つまり、Haskellの表記法またはScalaのシーケンス内包表記)。

4

2 に答える 2

4

バインド操作の定義プロパティの1つは、結果がまだモナド¹の「内部」にあることです。したがって、リストに対してバインドを実行すると、結果は再びリストになります。削減操作²はリスト以外のものになることが多いため、バインド操作で表現することはできません。

それに加えて、リストのバインド操作(つまり、concatMap / flatMap)は、一度に1つの要素のみを調べ、前の手順の結果を再利用する方法を提供しません。したがって、結果を単一要素リストにラップしても問題がない場合でも、モナド操作だけでそれを行う方法はありません。


¹したがって、モナド型クラスで定義されたもの以外の操作を実行できない型がある場合、モナドから「脱出」することはできません。これがIOモナドを機能させるものです。

ちなみに、HaskellとScalaではfoldと呼ばれています。

于 2012-06-08T22:43:49.697 に答える
3

bindがそれ自体でこれを実行できない場合、この操作を実行するための最も「モナディック」な方法は何ですか?

@ sepp2kによって与えられた答えは正しいですがreduce、リストに対して-like操作をモナド的に行う方法がありますが、製品または「ライター」モナドと、リストファンクターに製品モナドを配布することに対応する操作を使用します。

定義は次のとおりです。

import Control.Monad.Writer.Lazy
import Data.Monoid

reduce :: Monoid a => [a] -> a
reduce xs = snd . runWriter . sequence $ map tell xs 

開梱させてください:

  • Writerモナドのデータ型は、基本的に値と「書き込まれた」値Writer w aのタプル(積)です。書き込まれる値のタイプは、モナドのバインド操作が次のように定義されているモノイドである必要があります。awwWriter

        (w, a) >>= f = let (w', b) = f a in (mappend w w', b)
    

    つまり、入力された書き込み値と結果の書き込まれた値を取得し、モノイドの二項演算を使用してそれらを結合します。

  • 操作はtell値を書き込みますtell :: w -> Writer w ()。したがってmap tell、タイプが[a] -> [Writer a ()]あります。つまり、元のリストの各要素がモナドに「書き込まれた」モナディック値のリストです。

  • sequence :: Monad m => [m a] -> m [a]リストとモナドの間の分配法則に対応します。つまり、モナドタイプをリストタイプに分配します。sequenceバインドに関して、次のように定義できます。

    sequence [] = return []
    sequnece (x:xs) = x >>= (\x' -> (sequence xs) >>= (\xs' -> return $ x':xs'))
    

    実際には、Preludeusesfoldrでの実装、削減のような使用法の手がかり)

    したがって、sequence $ map tell xsタイプがありますWriter a [()]

  • この操作は、累積値を予測するためにここで構成されているタイプ、をrunWriterアンパックします。WriterrunWriter :: Writer w a -> (a, w)snd

のリストでIntの使用例は、monoidインスタンスを使用することです。

  instance Monoid Int where
              mappend = (+)
              mempty = 0

それから:

  > reduce ([1,2,3,4]::[Int])
  10
于 2012-06-09T21:39:28.503 に答える