【C#】F#のSeq.unfold関数をC#で実装してみた。

  • 2012.03.03 Saturday
  • 23:13
----------------------------------------------------------------------------
【関連】
【F#】Seq.unfoldは理解するのは難しいが・・・分かると簡単??
http://pro.art55.jp/?eid=1303926
【F#】Seq.unfoldは理解するのは難しいが・・・分かると簡単??その2
http://pro.art55.jp/?eid=1303927
【C#】F#のSeq.unfold関数をC#で実装してみた。
http://pro.art55.jp/?eid=1303928
【C#】F#のSeq.unfold関数をC#で実装してみた。その2
http://pro.art55.jp/?eid=1303929
-----------------------------------------------------------------------------

F#のSeq.unfold関数ですが、C#でそんなメソッドは見たこと事ありません。なのでC#で実装したら、どういうコードになるんだろうって思っちゃいますよね。なので書いてみました。

    public static class EnumerableUtils
    {
        public static IEnumerable<TResult> Unfold<TState, TResult>(TState currentState, Func<TState, Tuple<TResult, TState>> getCurrentResultAndNextSeed)
        {
            if (getCurrentResultAndNextSeed == null)
            {
                throw new ArgumentNullException("getCurrentResultAndNextSeed");
            }

            TState state = currentState;
            while (true)
            {
                Tuple<TResult, TState> tuple = getCurrentResultAndNextSeed(state);
                if (tuple != null)
                {
                    yield return tuple.Item1;
                    state = tuple.Item2;
                }
                else
                {
                    yield break;
                }
            }
        }
    }

Sytem.Tuple型をF#のタプルに見立てて書いてみたんですが・・・うーんどうなんでしょう。使いづらそうですが、とりあえず使ってみます。

        static void Main()
        {
            var result = EnumerableUtils.Unfold(Tuple.Create(0, 1), seed => Tuple.Create(seed.Item1, Tuple.Create(seed.Item2, seed.Item1 + seed.Item2)))
                .TakeWhile(number => number <= 100)
                .ToList();
            foreach (var i in result)
            {
                Console.WriteLine(i);
            }
        }

やっぱり・・・使いづらいですね。引数にタプルを渡すとか・・・正直しっくりこない。型が複雑すぎて記述するのが大変でした。型推論ならぬ型断定を自分でしなければならないのがつらい(笑)

ということで、Tupleは使う側には見えないようにしてみました。

    public static class EnumerableUtils
    {
        public static IEnumerable<TResult> Unfold<TState, TResult>(TState currentState, Func<TState, TResult> getCurrentResult, Func<TState, TState> getNextState)
        {
            return Unfold(currentState, getCurrentResult, getNextState, s => true);
        }

        public static IEnumerable<TResult> Unfold<TState, TResult>(TState currentState, Func<TState, TResult> getCurrentResult, Func<TState, TState> getNextState, Func<TState, bool> canNext)
        {
            if (getCurrentResult == null)
            {
                throw new ArgumentNullException("getCurrentResult");
            }

            if (getNextState == null)
            {
                throw new ArgumentNullException("getNextState");
            }

            return Unfold(currentState, state => canNext(state)
                                                     ? Tuple.Create(getCurrentResult(state), getNextState(state))
                                                     : null);
        }

        ...
    }
}

Func<TState, Tuple<TResult, TState>> getCurrentResultAndNextSeed

というデリゲートのを3つのデリゲートに分解しました。

Func<TState, TResult> getCurrentResult カレントの値を求める関数
Func<TState, TState> getNextState ネクストの状態を求める関数
Func<TState, bool> canNext ネクストの状態へ移行できるかどうは判定する関数

        static void Main()
        {
            var result = EnumerableUtils.Unfold(Tuple.Create(0, 1), currentState => currentState.Item1, currentState => Tuple.Create(currentState.Item2, currentState.Item1 + currentState.Item2), tuple => tuple.Item1 <= 100)
                .TakeWhile(number => number <= 100)
                .ToList();
            foreach (var i in result)
            {
                Console.WriteLine(i);
            }
        }

呼び出し元が多少呼び出しやすくなりました・・・。
で、まあせっかく分解したのでコードをきれいにしていくと。

つまり、unfoldがやってるのはこういうこと。

        public static IEnumerable<TResult> Unfold<TState, TResult>(TState currentState, Func<TState, TResult> getCurrentResult, Func<TState, TState> getNextState, Func<TState, bool> canNext)
        {
            for (TState state = currentState; canNext(state); state = getNextState(state))
            {
                yield return getCurrentResult(state);
            }
        }

for文で記述すると、unfoldってこういうことなのかー!?って新しい発見。
(このメソッドには欠点があります http://pro.art55.jp/?eid=1303929

------------------------------------------------
追記)
using System;
using System.Collections;
using System.Collections.Generic;

namespace Art55
{
    class Program
    {
        static void Main()
        {
            foreach (var i in new FibonacciCollction(100))
            {
                Console.WriteLine(i);
            }
        }

        class FibonacciCollction : IEnumerable<int>
        {
            public FibonacciCollction(int max)
            {
                _max = max;
            }

            private readonly int _max;

            public IEnumerator<int> GetEnumerator()
            {
                return EnumerableUtils.Unfold(FibonacciNumberPair.GetStartPair(),
                    currentState => currentState.Fn0,
                    currentState => FibonacciNumberPair.Create(currentState.Fn1, currentState.Fn0 + currentState.Fn1),
                    tuple => tuple.Fn0 <= _max).GetEnumerator();
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }

            class FibonacciNumberPair : IEquatable<FibonacciNumberPair>
            {
                private FibonacciNumberPair(int fn0, int fn1)
                {
                    _tuple = Tuple.Create(fn0, fn1);
                }

                public static FibonacciNumberPair Create(int fn0, int fn1)
                {
                    return new FibonacciNumberPair(fn0, fn1);
                }

                private static readonly FibonacciNumberPair StartPair = new FibonacciNumberPair(0, 1);
                public static FibonacciNumberPair GetStartPair()
                {
                    return StartPair;
                }

                private readonly Tuple<int, int> _tuple;
                public int Fn0 { get { return _tuple.Item1; } }
                public int Fn1 { get { return _tuple.Item2; } }

                public bool Equals(FibonacciNumberPair other)
                {
                    if (other == null)
                        return false;

                    return other._tuple == _tuple;
                }
            }
        }
    }

    public static class EnumerableUtils
    {
        public static IEnumerable<TResult> Unfold<TState, TResult>(TState currentState, Func<TState, TResult> getCurrentResult, Func<TState, TState> getNextState)
        {
            return Unfold(currentState, getCurrentResult, getNextState, s => true);
        }

        public static IEnumerable<TResult> Unfold<TState, TResult>(TState currentState, Func<TState, TResult> getCurrentResult, Func<TState, TState> getNextState, Func<TState, bool> canNext)
        {
            if (getCurrentResult == null)
            {
                throw new ArgumentNullException("getCurrentResult");
            }

            if (getNextState == null)
            {
                throw new ArgumentNullException("getNextState");
            }

            for (TState state = currentState; canNext(state); state = getNextState(state))
            {
                yield return getCurrentResult(state);
            }
        }
    }
}

--------------------
(追記)
シンプルなのがいいのかなー。

using System;
using System.Collections.Generic;

namespace Art55
{
    internal class Program
    {
        private static void Main()
        {
            foreach (int number in FibonacciSeq(100))
            {
                Console.WriteLine(number);
            }
        }

        public static IEnumerable<int> FibonacciSeq(int maxNumber)
        {
            for (int i = 0, j = 1, tmp; i <= maxNumber; tmp = j, j = i + j, i = tmp)
            {
                yield return i;
            }
        }
    }
}

-----------------------------
(追記)
書き方いろいろあるよね。

using System;
using System.Collections.Generic;

namespace Art55
{
    internal class Program
    {
        private static void Main()
        {
            foreach (int number in FibonacciSeq(100))
            {
                Console.WriteLine(number);
            }
        }

        public static IEnumerable<int> FibonacciSeq(int maxNumber)
        {
            for (var pair = new { F0 = 0, F1 = 1}; pair.F0 <= maxNumber; pair = new { F0 = pair.F1, F1 = pair.F0 + pair.F1 })
            {
                yield return pair.F0;
            }
        }
    }
}

コメント
コメントする








    
この記事のトラックバックURL
トラックバック

calendar

S M T W T F S
    123
45678910
11121314151617
18192021222324
252627282930 
<< November 2018 >>

あわせて読みたい

あわせて読みたいブログパーツ

selected entries

categories

archives

recent comment

  • 【キーボード】6年前のRealForceを復活させることはできる!?その3
    art55 (05/22)
  • 【キーボード】6年前のRealForceを復活させることはできる!?その3
    分解大好き (05/18)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    art55 (02/04)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    Gen (02/04)
  • 【キーボード】RealForce が壊れて帰ってきた。
    art55 (04/29)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    art55 (02/23)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    かるあ (02/22)
  • 【C#】Dictionaryの実装・データ構造・アルゴリズムを観察する。
    art55 (01/16)
  • 【C#】Dictionaryの実装・データ構造・アルゴリズムを観察する。
    karuakun (01/16)
  • 【NetOffice】【Excel】死なないExcelプロセスをKillする。
    art55 (12/05)

recent trackback

recommend

recommend

recommend

C#プログラマのための.NETアプリケーション最適化技法 (Programmer's SELECTION)
C#プログラマのための.NETアプリケーション最適化技法 (Programmer's SELECTION) (JUGEMレビュー »)
Sasha Goldshtein,Dima Zurbalev,Ido Flatow,サシャ・ゴルドシュタイン,ディマ・ズルバレフ,イド・フラトー

recommend

ろんりと集合
ろんりと集合 (JUGEMレビュー »)
中内 伸光
とてもわかりやすいです。

recommend

recommend

シャノン・ノイマン・ディジタル世界
シャノン・ノイマン・ディジタル世界 (JUGEMレビュー »)
市川 忠男
4章がリレーショナルデータベースな内容になってます。ページ数があまりありませんが、ポイントがものすごく的確にまとまっていて、感動します。

recommend

recommend

東プレ Realforce91UBK-S 静音キーボード 静電容量無接点方式 変荷重 ブラック NG01BS
東プレ Realforce91UBK-S 静音キーボード 静電容量無接点方式 変荷重 ブラック NG01BS (JUGEMレビュー »)

テンキーレス、静音のRealForce91UBK-S。スコスコ感がたまらなく気持ちいいです。家と会社で2台持ってます。

recommend

recommend

プログラミング.NET Framework 第4版 (プログラミングシリーズ)
プログラミング.NET Framework 第4版 (プログラミングシリーズ) (JUGEMレビュー »)
Jeffrey Richter
発売予定美 2013年10月10日。.NET Frameworkとお付き合いする人のバイブルですね。

recommend

recommend

キャット・シッターの君に。
キャット・シッターの君に。 (JUGEMレビュー »)
喜多嶋 隆
私のイラストレータデビュー本です。

recommend

Essential .NET ― 共通言語ランタイムの本質
Essential .NET ― 共通言語ランタイムの本質 (JUGEMレビュー »)
ドン・ボックス,クリス・セルズ,Don Box,Chris Sells,吉松 史彰

links

profile

search this site.

others

mobile

qrcode

powered

無料ブログ作成サービス JUGEM