非専門的シンギュラリティー研究所

無限に動き続けるシステムを表す方法を AI なども使って考えていきます。

自由モノイドのイテレーター(32)

自由モノイドプログラミング言語の作成(MonIter バージョン1.2 仕様(16))

前回までの説明では、線形リストのクラスは独立していなかったのですが、クラスの構成を変えて独立させました。

線形リストのクラス

変数とその値の対応を記憶するために(一方向の)線形リストを使います。このシステムでは線形リストである必要はないのですが、別のシステムでも使うためあらかじめ線形リストにしておきます。Wikipediaによると線形リストには片方向リストと双方向リストがあります。ここで使うのは片方向リスト(これが一般的な用語かどうかはよくわかりません)となります。

線形リストの終端は null とします。

線形リストの実体のノードのクラス(LinearListNode<T>)

LinearListNode<T> は IEnumerable<T> を実装するクラスとします。

    /// <summary>
    /// 線形リストのノードのクラス
    /// </summary>
    /// <typeparam name="T">要素の型</typeparam>
    internal class LinearListNode<T> : IEnumerable<T>
    {
        /// <summary>
        /// このノードの要素
        /// </summary>
        private readonly T item;
        /// <summary>
        /// 線形リストの次のノード(終端は null)
        /// </summary>
        private readonly LinearListNode<T>? next_node;
        public LinearListNode(T item, LinearListNode<T>? next_node)
        {
            this.item = item;
            this.next_node = next_node;
        }
        /// <summary>
        /// このノードの要素
        /// </summary>
        public T Item => item;
        /// <summary>
        /// 線形リストの次のノード(終端は null)
        /// </summary>
        internal LinearListNode<T>? NextNode => next_node;
        /// <summary>
        /// IEnumerable 実装のために必要なイテレーターを取得するメソッド
        /// </summary>
        /// <returns>IEnumerable のイテレーター</returns>
        public IEnumerator<T> GetEnumerator()
        {
            IEnumerable<T> ListEnum()
            {
                for (LinearListNode<T>? node = this; node != null; node = node.next_node)
                {
                    yield return node.item;
                }
            }
            return ListEnum().GetEnumerator();
        }
        /// <summary>
        /// IEnumerable 実装のために必要なイテレーターを取得するメソッド
        /// </summary>
        /// <returns>IEnumerable のイテレーター</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
線形リストのクラス(LinearList<T>)

LinearList<T> は IEnumerable<T> を実装するクラスとします。

LinearList<T> は内部で線形リストの実体(LinearListNode<T>)を保持します。LinearListNode<T> では null の場合の処理ができないのでこのクラスが必要となります。foreach の中で null が使えるというのを見たことがあるような気がしたのですが「C# foreach null」で検索しても見つかりませんでした。

Add メソッドは、これがあるとコレクション式で初期化できるようになるため実装していますが、ここでは使っていません。

    /// <summary>
    /// 線形リストのクラス
    /// </summary>
    /// <typeparam name="T">要素の型</typeparam>
    internal class LinearList<T> : IEnumerable<T>
    {
        /// <summary>
        /// 線形リストのノード
        /// </summary>
        private LinearListNode<T>? list_node;
        /// <summary>
        /// 変数と変数の値の対応を初期化:ノードの終端は null
        /// </summary>
        public LinearList()
        {
            list_node = null;
        }
        /// <summary>
        /// IEnumerable 実装のために必要なイテレーターを取得するメソッド
        /// </summary>
        /// <returns>IEnumerable のイテレーター</returns>
        public IEnumerator<T> GetEnumerator()
        {
            if (list_node == null)
            {
                return new List<T> { }.GetEnumerator();
            }
            else
            {
                return list_node.GetEnumerator();
            }
        }
        /// <summary>
        /// IEnumerable 実装のために必要なイテレーターを取得するメソッド
        /// </summary>
        /// <returns>IEnumerable のイテレーター</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
        /// <summary>
        /// 最後に要素を追加したノードを取得する
        /// </summary>
        /// <param name="list_node">要素を追加するノード</param>
        /// <param name="item">追加する要素</param>
        /// <returns>最後に要素を追加したノード</returns>
        private LinearListNode<T>? AddItem(LinearListNode<T>? list_node, T item)
        {
            if (list_node == null)
            {
                return new LinearListNode<T>(item, null);
            }
            else
            {
                return new LinearListNode<T>(list_node.Item, AddItem(list_node.NextNode, item));
            }
        }
        /// <summary>
        /// 最後に要素を追加する(コレクション式で初期化できるようにするため)
        /// </summary>
        /// <param name="item">追加する要素</param>
        public void Add(T item)
        {
            list_node = AddItem(list_node, item);
        }
        /// <summary>
        /// 先頭に要素を追加した新しいリストを作る
        /// </summary>
        /// <param name="item">追加する要素</param>
        /// <returns>先頭に要素を追加した新しいリスト</returns>
        public LinearList<T> Define(T item)
        {
            LinearList<T> new_list = new LinearList<T>();
            new_list.list_node = new LinearListNode<T>(item, list_node);
            return new_list;
        }
    }