Enum 組込みオブジェクト

この章では組込み変数領域が初めから備えている Enum 組込みオブジェクトを定義する。

Enum はスクリプトで扱われる列挙オブジェクト に対応する Type のインスタンスである。

列挙オブジェクト

列挙オブジェクトとは、値を順番に取出す操作が行えるオブジェクトである。値を順番に取り出す操作 (列挙) は toNext メソッドitem プロパティによって行う。

列挙オブジェクトは、0 個以上のオブジェクトを列挙する。列挙の内容は 列挙オブジェクトによって異なる。一般に、 列挙オブジェクトが列挙するオブジェクトは有限個とは限らない。

列挙オブジェクトは、全て Enum のインスタンスである。

開始列挙オブジェクトとは列挙オブジェクトの一種であり、 オブジェクトの列挙を開始する始まりの列挙オブジェクトである。

Enum のメンバ

prototype
Object.prototype を準プロトタイプとするオブジェクト。
name
文字列オブジェクト "Enum"
map メソッド
次の関数生成式を評価して得られるような関数オブジェクト:
@[func, enum] {
   @if $args.count == 0:
      @throw NumberOfArgumentsError[];
   @var enums = $args;
   enums.shift[];
   @var toNext = @{
      @for i : 1 ~ enums.count:
         @if !(enums[i] = enums[i].toNext[]):
            @return enums[i];
      @this.item = func[[enums.map[& #.item]]];
      @return @this;
   };
   @return Enum[toNext, @this];
}

このメソッドは、いくつかの列挙オブジェクトで同時に 列挙を行い、それぞれの列挙オブジェクトで列挙されたオブジェクトを引数として 関数を呼出し、その結果を列挙する列挙オブジェクトを返す。

例えば、以下のコードを実行すると、2, 6, 12, 20, …, 110 を表す整数オブジェクトを要素とするリストオブジェクトが得られる。

Enum.map[& #1 * #2, 1 ~ 10, 2 ~ 11].toList[]
zip メソッド
次の関数生成式を評価して得られるような関数オブジェクト:
@[enum1, enum2] {
   $args.prepend[List.of];
   @return Enum.map[[$args]];
}
このメソッドは、いくつかの列挙オブジェクトで同時に 列挙を行い、それぞれの列挙オブジェクトで列挙されたオブジェクトを要素とする リストオブジェクトを列挙する列挙オブジェクトを返す。

Enum の内部メンバ

$prototype
Type.prototype

Enum の関数としての呼出し

Enum は関数として呼出し可能であり、 関数として呼出されると次の処理を行う:

  1. 呼出しにおける引数が 0 個ならば新しい NumberOfArgumentsError のインスタンスを投げる。
  2. 新しいオブジェクトを作成し、それを E とする。この時点では、 このオブジェクトは一切メンバ・内部メンバを持たない。このオブジェクトは 関数として呼出し可能でない。
  3. E$prototype 内部メンバを作成し、その値を Enum.prototype とする。
  4. EtoNext という名前のメンバを作成し、呼出しにおける一つ目の引数をその値とする。
  5. 呼出しにおける引数が二つ以上ならば、E#source という名前のメンバを作成し、二つ目の引数をその値とする。
  6. [[E]] を返す。

Enum の関数としての呼出しは、 二つ目の引数を元にして、一つ目の引数として与えられた関数オブジェクトを toNext メソッドとして使って列挙を行う簡単な開始列挙オブジェクトを作成する。

列挙オブジェクトのプロパティ

列挙オブジェクトは以下のプロパティを持つものと見なす。

toNext メソッド
このメソッドが引数無しで関数として呼出されると、 次のようなオブジェクトを値とする正常終了の結果を返す: このメソッドが引数ありで呼出された時の動作は規定しないが、 引数無しで呼出されたときと同様に動作するのが望ましい。
item
開始列挙オブジェクトについては、このプロパティは規定しない。 すなわち、このプロパティは存在しないか、 あるいは存在しても値は一般に何であるか決まりはない。
開始列挙オブジェクトでない列挙オブジェクトについては、toNext メソッドが呼出される前はこのプロパティは必ず存在し、 その値は列挙されるオブジェクトである。スクリプトによって直接 代入または削除されない限り、このプロパティの値は変化してはならない。

これらのプロパティは読み取り専用でなくても良い。列挙オブジェクトの プロパティが toNext メソッド内部以外のところから代入もしくは 削除されたならば、それ以降その列挙オブジェクトはオブジェクトを 上述の規定通りに正しく列挙しなくても良い。

列挙オブジェクトの toNext メソッドが一度でも関数として呼出された 後は、そのオブジェクトは列挙オブジェクトでなくなってもよい。

注意: 列挙オブジェクトの toNext メソッドが呼出された後は、toNext/item プロパティに関する規定はない。 これを利用して、列挙オブジェクトを使い回すような動作が一般に認められる。 すなわち、toNext メソッドが呼出されたときに (新しい列挙オブジェクトを作るのではなくて) その列挙オブジェクトの item プロパティの値を変更した上で 同じ列挙オブジェクトをそのまま返すような実装が認められる。

Enum.prototype のプロパティ

head メソッド
次の関数生成式を評価して得られるような関数オブジェクト:
@[count] {
   @var toNext = @{
      @if --count < 0:
         @return @null;
      #source = #source.toNext[];
      @if #source: {
         @this.item = #source.item;
         @return @this;
      } @else: {
         @return #source;
      }
   };
   @return Enum[toNext, @this];
}
このメソッドは、この列挙オブジェクトが列挙する オブジェクトのうち最初のいくつかだけを列挙する列挙オブジェクトを返す。
skip メソッド
次の関数生成式を評価して得られるような関数オブジェクト:
@[count] {
   @var toNext = @{
      @do:
         #source = #source.toNext[];
      @while #source && --count >= 0;
      @return #source;
   };
   @return Enum[toNext, @this];
}
このメソッドは、この列挙オブジェクトが列挙する オブジェクトのうち最初のいくつかを飛ばして列挙する列挙オブジェクトを返す。
map メソッド
次の関数生成式を評価して得られるような関数オブジェクト:
@[f] {
   @var toNext = @{
      #source = #source.toNext[];
      @if #source: {
         @this.item = f[#source.item];
         @return @this;
      } @else: {
         @return #source;
      }
   };
   @return Enum[toNext, @this];
}
このメソッドは、この列挙オブジェクトが列挙する オブジェクトに引数関数を適用した結果を列挙する新しい開始列挙オブジェクトを返す。
filter メソッド
次の関数生成式を評価して得られるような関数オブジェクト:
@[f] {
   @var toNext = @{
      @while (#source = #source.toNext[]) && !f[#source.item]: ;
      @if #source: {
         @this.item = #source.item;
         @return @this;
      } @else: {
         @return #source;
      }
   };
   @return Enum[toNext, @this];
}
このメソッドは、この列挙オブジェクトが列挙する オブジェクトのうち、引数で示された述語を満たすオブジェクトのみに限定した列挙を 行う新しい開始列挙オブジェクトを返す。
forAll メソッド
次の関数生成式を評価して得られるような関数オブジェクト:
@[predicate] {
   @if ## == 0:
      predicate = Function.id;
   @for i: @this:
      @if !predicate[i]:
         @return @false;
   @return @true;
}
このメソッドは、この列挙オブジェクトで実際に列挙操作を 行い、引数で示された述語を全てのオブジェクトが満たしているかどうかを調べる。
exists メソッド
次の関数生成式を評価して得られるような関数オブジェクト:
@[predicate] {
   @if ## == 0:
      predicate = Function.id;
   @for i: @this:
      @if predicate[i]:
         @return @true;
   @return @false;
}
このメソッドは、この列挙オブジェクトで実際に列挙操作を 行い、引数で示された述語を満たすオブジェクトが存在するかどうかを調べる。
fold メソッド
次の関数生成式を評価して得られるような関数オブジェクト:
@[func, init] {
   @var e = @this;
   @if !@exists $args[2]: {
      e = e.toNext[];
      @if !e:
         @throw UnsupportedOperationError[];
      init = e.item;
   }
   @var v = init;
   @for i: e:
      v = func[v, i];
   @return v;
}

このメソッドは、この列挙オブジェクトで実際に列挙操作を行い、 引数で指定されたに引数関数を用いて各オブジェクトを畳み込む。 第二引数が指定された場合は、それが初期値となる。第二引数が渡されなければ、 最初に列挙されるオブジェクトが初期値となる。

例えば以下のコードは、1 * 2 * 3 * 4 の計算を行い、結果として 24 を返す:

List.of[1, 2, 3, 4].fold[& #1 * #2]

また以下のコードは、0 + 1 + 2 + 3 + 4 の計算を行い、結果として 10 を返す:

List.of[1, 2, 3, 4].fold[& #1 + #2, 0]
toList メソッド
次の関数生成式を評価して得られるような関数オブジェクト: @ { List.of[[@this]] }
このメソッドは、この列挙オブジェクトで実際に列挙操作を 行い、列挙されたオブジェクトを全て含む新しいリストを返す。

列挙オブジェクトの関数としての呼出し

列挙オブジェクトが関数として呼出し可能であるかどうか、 および関数として呼出されたときの動作は、一般には規定しない。

列挙動作

オブジェクト O を開始列挙オブジェクトと見なして列挙動作を行い、 全ての列挙結果を得る処理 enumerate-all を、以下のように定義する:

  1. l を要素を含まない原始リストとする。
  2. [[O]] を e と置く。
  3. *1: 式 E.toNext[] を評価し、その結果に対して get-reference-value を行い、その結果を改めて e と置く。ただし、この評価において部分式 E の評価結果は e であるとする。
  4. e が正常終了でなければ、それを返す。
  5. e の値に対して to-boolean を実行し、その結果が false ならば [[l]] を返す。
  6. e の値の item プロパティを取得する。 取得の結果が正常終了でなければ、直ちにそれを返す。正常終了ならば、 その値を V とする。
  7. l の末尾に V を追加して出来る新たな原始リストを改めて l とする。
  8. E.toNext[] の評価 (*1) に戻る。

ただし、Oリストオブジェクトならば、上記の代わりに以下の動作を行ってもよい:

  1. [[Oelements 内部メンバの値]] を返す。

列挙オブジェクトの例

リストオブジェクトに含まれるオブジェクトを列挙し、各オブジェクトを出力する例:

@var list = List.of[1, 2, 3];
@var e = list.getEnum[];
@while e = e.toNext[]:
   @printline e.item;

または:

@var list = List.of[1, 2, 3];
@for i : list:
   @printline i;
© 2006-2007 Magicant