LoginSignup
1
1

More than 1 year has passed since last update.

ジェネレーターでリストモナドを模倣してみた

Last updated at Posted at 2014-12-18

Haskellの実験メモです。

ECMAScript 6のジェネレーターでHaskellのリストモナドを模倣してみました。

一重

Haskell
main = do
    print $ do
        x <- [1..5]
        return $ x * 2
実行結果
[2,4,6,8,10]

これになるべく近くなるように模倣してみました。ジェネレーターやforofは動作環境を選びます。

ES2015
console.log(Array.from(function*() {
    for (let x of [1, 2, 3, 4, 5]) {
        yield x * 2;
    }
}()));
実行結果
[ 2, 4, 6, 8, 10 ]

参考までに、ジェネレーターを使わない例を示します。

ES2015
console.log(function() {
    let ret = [];
    for (let x of [1, 2, 3, 4, 5]) {
        ret.push(x * 2);
    }
    return ret;
}());
実行結果
[ 2, 4, 6, 8, 10 ]

この方式は以下の記事で使用しています。

二重

Haskell
main = do
    print $ do
        x <- [1..3]
        y <- "abc"
        return (x, y)
実行結果
[(1,'a'),(1,'b'),(1,'c'),(2,'a'),(2,'b'),(2,'c'),(3,'a'),(3,'b'),(3,'c')]

同じ方針で模倣します。

ES2015
console.log(Array.from(function*() {
    for (let x of [1, 2, 3]) {
        for (let y of "abc") {
            yield [x, y];
        }
    }
}()));
実行結果
[ [ 1, 'a' ],
  [ 1, 'b' ],
  [ 1, 'c' ],
  [ 2, 'a' ],
  [ 2, 'b' ],
  [ 2, 'c' ],
  [ 3, 'a' ],
  [ 3, 'b' ],
  [ 3, 'c' ] ]

タプルがないため配列で代用しています。

フラット化

Haskellではreturnではなく複数の項目を含むリストを返すとフラット化されます。LINQのSelectManyやScalaのflatMapと関係があります。

タプルと比較します。リストにする関係上、要素の型は同じにします。

Haskell
main = do
    print $ do
        x <- [1,2]
        y <- [3,4]
        return (x, y)
    print $ do
        x <- [1,2]
        y <- [3,4]
        [x, y]
実行結果
[(1,3),(1,4),(2,3),(2,4)]
[1,3,1,4,2,3,2,4]

joinreturnを省略しているとも解釈できます。リスト内包表記で結果をフラットにするにはjoin必須です。

Haskell
import Control.Monad

main = do
    print $ do
        x <- [1,2]
        y <- [3,4]
        return [x, y]
    print [[x, y] | x <- [1,2], y <- [3,4]]
    print $ join $ do
        x <- [1,2]
        y <- [3,4]
        return [x, y]
    print $ join $ [[x, y] | x <- [1,2], y <- [3,4]]
実行結果
[[1,3],[1,4],[2,3],[2,4]]
[[1,3],[1,4],[2,3],[2,4]]
[1,3,1,4,2,3,2,4]
[1,3,1,4,2,3,2,4]

ジェネレーターで模倣するにはyieldを複数回に分けるのが簡単です。

ES2015
console.log(Array.from(function*() {
    for (let x of [1, 2]) {
        for (let y of [3, 4]) {
            yield x;
            yield y;
        }
    }
}()));
実行結果
[ 1, 3, 1, 4, 2, 3, 2, 4 ]

関連記事

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1