「イテレータ」の版間の差分
編集の要約なし タグ: コメントアウト モバイル編集 モバイルウェブ編集 改良版モバイル編集 |
|||
26行目: | 26行目: | ||
[[C++]]では、[[Standard Template Library|STL]]が外部イテレータの枠組みを定義している。この枠組みは[[ポインタ (プログラミング)|ポインタ]]と互換性を持つよう定められているため、ポインタを用いてイテレータを構成することができる。 |
[[C++]]では、[[Standard Template Library|STL]]が外部イテレータの枠組みを定義している。この枠組みは[[ポインタ (プログラミング)|ポインタ]]と互換性を持つよう定められているため、ポインタを用いてイテレータを構成することができる。 |
||
< |
<syntaxhighlight lang="cpp"> |
||
template<typename InputIterator> |
template<typename InputIterator> |
||
void printall(InputIterator first, InputIterator last) |
void printall(InputIterator first, InputIterator last) |
||
35行目: | 35行目: | ||
} |
} |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
なお、イテレータの種類によって用いることのできる演算子に違いがある。例えば、<code>std::vector</code>コンテナのイテレータ([[ランダムアクセス]]イテレータ)と<code>std::list</code>コンテナのイテレータ(双方向イテレータ)は共に[[インクリメント]]/デクリメントを用いて次/前の要素を指すことができるが、任意の加算/減算は前者でのみ定義されている。これは<code>std::list</code>コンテナの要素が、前後の要素へのポインタしか保持していないため[[シーケンシャルアクセス]]しかできず、n 個先の要素への移動が定数時間で実行できないからである。 |
なお、イテレータの種類によって用いることのできる演算子に違いがある。例えば、<code>std::vector</code>コンテナのイテレータ([[ランダムアクセス]]イテレータ)と<code>std::list</code>コンテナのイテレータ(双方向イテレータ)は共に[[インクリメント]]/デクリメントを用いて次/前の要素を指すことができるが、任意の加算/減算は前者でのみ定義されている。これは<code>std::list</code>コンテナの要素が、前後の要素へのポインタしか保持していないため[[シーケンシャルアクセス]]しかできず、n 個先の要素への移動が定数時間で実行できないからである。 |
||
42行目: | 42行目: | ||
[[Delphi]]では、バージョン2005より<code>for-in</code>構文によるイテレータがある。ユーザーによるイテレータは<code>MoveNext</code>メソッドや<code>Current</code>プロパティを任意のクラス等に実装することで定義でき、型に厳格な[[Pascal]]系言語ながらこれらを実装するだけで<code>for-in</code>により認識されるという[[ダックタイピング]]にも似た仕組みとなっている。 |
[[Delphi]]では、バージョン2005より<code>for-in</code>構文によるイテレータがある。ユーザーによるイテレータは<code>MoveNext</code>メソッドや<code>Current</code>プロパティを任意のクラス等に実装することで定義でき、型に厳格な[[Pascal]]系言語ながらこれらを実装するだけで<code>for-in</code>により認識されるという[[ダックタイピング]]にも似た仕組みとなっている。 |
||
< |
<syntaxhighlight lang="delphi"> |
||
for item in items do |
for item in items do |
||
Writeln(item); |
Writeln(item); |
||
</syntaxhighlight> |
|||
</source> |
|||
=== Java === |
=== Java === |
||
[[Java]]では、{{Javadoc:SE|name=java.util.Iterator|java/util|Iterator}}インターフェイス族を実装するオブジェクトが外部イテレータとなる。Java 1.5以降の<code>Iterator</code>は[[ジェネリクス]]に対応している。 |
[[Java]]では、{{Javadoc:SE|name=java.util.Iterator|java/util|Iterator}}インターフェイス族を実装するオブジェクトが外部イテレータとなる。Java 1.5以降の<code>Iterator</code>は[[ジェネリクス]]に対応している。 |
||
< |
<syntaxhighlight lang="java"> |
||
import java.util.*; |
import java.util.*; |
||
... |
... |
||
64行目: | 64行目: | ||
System.out.println(obj.toString()); |
System.out.println(obj.toString()); |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
Java 1.5以降では、{{Javadoc:SE|name=java.lang.Iterable|java/lang|Iterable}}インターフェイスを実装すると、拡張for文([[foreach文]])の対象にすることができる。 |
Java 1.5以降では、{{Javadoc:SE|name=java.lang.Iterable|java/lang|Iterable}}インターフェイスを実装すると、拡張for文([[foreach文]])の対象にすることができる。 |
||
< |
<syntaxhighlight lang="java"> |
||
// java.util.List は Iterable を実装している。 |
// java.util.List は Iterable を実装している。 |
||
for (final Object obj : list) { |
for (final Object obj : list) { |
||
System.out.println(obj.toString()); |
System.out.println(obj.toString()); |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
=== Perl === |
=== Perl === |
||
[[Perl]]には、<code>foreach</code>、<code>each</code> といった繰り返しのキーワードがある。 |
[[Perl]]には、<code>foreach</code>、<code>each</code> といった繰り返しのキーワードがある。 |
||
他に、Tie機能 (変数操作のオーバーロード) でユーザーデータに対するイテレータを定義できる。 |
他に、Tie機能 (変数操作のオーバーロード) でユーザーデータに対するイテレータを定義できる。 |
||
< |
<syntaxhighlight lang="perl"> |
||
# foreachを使った例。配列・リストに対する反復 |
# foreachを使った例。配列・リストに対する反復 |
||
foreach my $element(@array){ |
foreach my $element(@array){ |
||
print $element, "\n"; |
print $element, "\n"; |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
< |
<syntaxhighlight lang="perl"> |
||
# eachを使った例。ハッシュ(連想配列)に対する反復 |
# eachを使った例。ハッシュ(連想配列)に対する反復 |
||
while(my($key, $value) = each %hash){ |
while(my($key, $value) = each %hash){ |
||
print "$key=$value\n"; |
print "$key=$value\n"; |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
=== PHP === |
=== PHP === |
||
95行目: | 95行目: | ||
また、大抵のオブジェクトにはあらかじめイテレータが実装されている。 |
また、大抵のオブジェクトにはあらかじめイテレータが実装されている。 |
||
< |
<syntaxhighlight lang="php"> |
||
# foreachを使った例。配列・連想配列・オブジェクト等に対し全く同じように使用できる。 |
# foreachを使った例。配列・連想配列・オブジェクト等に対し全く同じように使用できる。 |
||
foreach ( $elements as $key=>$value ){ |
foreach ( $elements as $key=>$value ){ |
||
print $key . "=" . $value . " \n"; |
print $key . "=" . $value . " \n"; |
||
} |
} |
||
</syntaxhighlight> |
|||
</source> |
|||
=== Python === |
=== Python === |
||
109行目: | 109行目: | ||
Pythonのfor文においてはiterableを範囲にとって、暗黙的にiteratorを利用する点を指して内部イテレータと呼ばれる場合もある。 |
Pythonのfor文においてはiterableを範囲にとって、暗黙的にiteratorを利用する点を指して内部イテレータと呼ばれる場合もある。 |
||
< |
<syntaxhighlight lang="python"> |
||
cont = iteratable_container() |
cont = iteratable_container() |
||
125行目: | 125行目: | ||
for element in cont: |
for element in cont: |
||
print element |
print element |
||
</syntaxhighlight> |
|||
</source> |
|||
また、Pythonには一種の[[コルーチン]]を記述できる[[ジェネレータ (プログラミング)|ジェネレータ]]もある。ジェネレータはイテレータを返す関数で、yield文により、<code>__next__()</code>で実行される手続きを次々と記述できる。 |
また、Pythonには一種の[[コルーチン]]を記述できる[[ジェネレータ (プログラミング)|ジェネレータ]]もある。ジェネレータはイテレータを返す関数で、yield文により、<code>__next__()</code>で実行される手続きを次々と記述できる。 |
||
< |
<syntaxhighlight lang="python"> |
||
def fruit_generator(): |
def fruit_generator(): |
||
yield 'banana' # 最初の __next__() によりここまで実行され 'banana' を返す |
yield 'banana' # 最初の __next__() によりここまで実行され 'banana' を返す |
||
143行目: | 143行目: | ||
print(next(it)) |
print(next(it)) |
||
print(next(it)) # この行で StopIteration 例外になる |
print(next(it)) # この行で StopIteration 例外になる |
||
</syntaxhighlight> |
|||
</source> |
|||
=== Ruby === |
=== Ruby === |
||
150行目: | 150行目: | ||
ブロックを付けて呼ばれたメソッド中でyieldを実行すると、ブロックの中の手続きが実行される。Pythonのジェネレータと同じyieldというキーワードで、コードの見た目も似たものになるが、具体的な働きは全く異なることに注意。 |
ブロックを付けて呼ばれたメソッド中でyieldを実行すると、ブロックの中の手続きが実行される。Pythonのジェネレータと同じyieldというキーワードで、コードの見た目も似たものになるが、具体的な働きは全く異なることに注意。 |
||
< |
<syntaxhighlight lang="ruby"> |
||
class MyObj |
class MyObj |
||
def my123 |
def my123 |
||
168行目: | 168行目: | ||
p x |
p x |
||
end |
end |
||
</syntaxhighlight> |
|||
</source> |
|||
=== .NET言語 === |
=== .NET言語 === |
||
176行目: | 176行目: | ||
==== C# 2.0 ==== |
==== C# 2.0 ==== |
||
< |
<syntaxhighlight lang="csharp"> |
||
// 明示的な使い方 |
// 明示的な使い方 |
||
IEnumerator<MyType> iter = list.GetEnumerator(); |
IEnumerator<MyType> iter = list.GetEnumerator(); |
||
185行目: | 185行目: | ||
foreach (MyType value in list) |
foreach (MyType value in list) |
||
Console.WriteLine(value); |
Console.WriteLine(value); |
||
</syntaxhighlight> |
|||
</source> |
|||
C# 2.0およびVB.NET 11は反復子の形で[[ジェネレータ (プログラミング)|ジェネレータ]] (generator) をサポートする。ジェネレータは<code>IEnumerator</code>または<code>IEnumerable</code>を返すよう宣言されたメソッドであるが、オブジェクトインスタンスを返す代わりに要素のシーケンスを生成するための<code>yield return</code>ステートメントを使用する。yieldステートメントを用いて記述されたジェネレータはコンパイラによって、適切なインターフェイスを実装する新しいクラスに変換される。ただし、ジェネレータ(反復子)は<code>IEnumerator.Reset()</code>メソッドをサポートしない。 |
C# 2.0およびVB.NET 11は反復子の形で[[ジェネレータ (プログラミング)|ジェネレータ]] (generator) をサポートする。ジェネレータは<code>IEnumerator</code>または<code>IEnumerable</code>を返すよう宣言されたメソッドであるが、オブジェクトインスタンスを返す代わりに要素のシーケンスを生成するための<code>yield return</code>ステートメントを使用する。yieldステートメントを用いて記述されたジェネレータはコンパイラによって、適切なインターフェイスを実装する新しいクラスに変換される。ただし、ジェネレータ(反復子)は<code>IEnumerator.Reset()</code>メソッドをサポートしない。 |
||
< |
<syntaxhighlight lang="csharp"> |
||
// 反復子の記述例。 |
// 反復子の記述例。 |
||
static IEnumerable<int> MyIteratorMethod() { |
static IEnumerable<int> MyIteratorMethod() { |
||
203行目: | 203行目: | ||
foreach (int element in elements) |
foreach (int element in elements) |
||
Console.WriteLine(element); |
Console.WriteLine(element); |
||
</syntaxhighlight> |
|||
</source> |
|||
==== Visual Basic 8.0 ==== |
==== Visual Basic 8.0 ==== |
||
< |
<syntaxhighlight lang="vbnet"> |
||
' 明示的な使い方 |
' 明示的な使い方 |
||
Dim iter As IEnumerator(Of MyType) = list.GetEnumerator() |
Dim iter As IEnumerator(Of MyType) = list.GetEnumerator() |
||
217行目: | 217行目: | ||
Console.WriteLine(value) |
Console.WriteLine(value) |
||
Next value |
Next value |
||
</syntaxhighlight> |
|||
</source> |
|||
=== D言語 === |
=== D言語 === |
||
[[D言語]]では、標準ライブラリにレンジ (Range) というイテレータが定義されており、規定されたインターフェイスを持っているオブジェクトなら何でもレンジとして扱うことができる。 |
[[D言語]]では、標準ライブラリにレンジ (Range) というイテレータが定義されており、規定されたインターフェイスを持っているオブジェクトなら何でもレンジとして扱うことができる。 |
||
< |
<syntaxhighlight lang="D"> |
||
foreach (item; range) |
foreach (item; range) |
||
writeln(item); |
writeln(item); |
||
</syntaxhighlight> |
|||
</source> |
|||
== 脚注 == |
== 脚注 == |
2020年7月5日 (日) 22:40時点における版
イテレータ(英語: iterator)とは、プログラミング言語において配列やそれに類似する集合的データ構造(コレクションあるいはコンテナ)の各要素に対する繰り返し処理の抽象化である。実際のプログラミング言語では、オブジェクトまたは文法などとして現れる。JISでは反復子(はんぷくし)と翻訳されている[1][2]。
ジェネレータ (プログラミング) の記事も参照のこと。
種類
内部イテレータ
hashなどのオブジェクトが高階関数として実装しているイテレータのことを内部イテレータという。 主にOOPの性質に基づき匿名性のあるイテレータ(通常外部から明示的に使用することはないもの)のことを指す。
各言語における例
C++
C++では、STLが外部イテレータの枠組みを定義している。この枠組みはポインタと互換性を持つよう定められているため、ポインタを用いてイテレータを構成することができる。
template<typename InputIterator>
void printall(InputIterator first, InputIterator last)
{
for(; first != last; ++first)
{
std::cout << *first << std::endl;
}
}
なお、イテレータの種類によって用いることのできる演算子に違いがある。例えば、std::vector
コンテナのイテレータ(ランダムアクセスイテレータ)とstd::list
コンテナのイテレータ(双方向イテレータ)は共にインクリメント/デクリメントを用いて次/前の要素を指すことができるが、任意の加算/減算は前者でのみ定義されている。これはstd::list
コンテナの要素が、前後の要素へのポインタしか保持していないためシーケンシャルアクセスしかできず、n 個先の要素への移動が定数時間で実行できないからである。
Delphi
Delphiでは、バージョン2005よりfor-in
構文によるイテレータがある。ユーザーによるイテレータはMoveNext
メソッドやCurrent
プロパティを任意のクラス等に実装することで定義でき、型に厳格なPascal系言語ながらこれらを実装するだけでfor-in
により認識されるというダックタイピングにも似た仕組みとなっている。
for item in items do
Writeln(item);
Java
Javaでは、java.util.Iterator
インターフェイス族を実装するオブジェクトが外部イテレータとなる。Java 1.5以降のIterator
はジェネリクスに対応している。
import java.util.*;
...
final List list = new ArrayList();
list.add("hoge");
list.add(Integer.valueOf(100));
list.add(Double.valueOf(-0.5));
final Iterator it = list.iterator();
while (it.hasNext()) {
final Object obj = it.next();
System.out.println(obj.toString());
}
Java 1.5以降では、java.lang.Iterable
インターフェイスを実装すると、拡張for文(foreach文)の対象にすることができる。
// java.util.List は Iterable を実装している。
for (final Object obj : list) {
System.out.println(obj.toString());
}
Perl
Perlには、foreach
、each
といった繰り返しのキーワードがある。
他に、Tie機能 (変数操作のオーバーロード) でユーザーデータに対するイテレータを定義できる。
# foreachを使った例。配列・リストに対する反復
foreach my $element(@array){
print $element, "\n";
}
# eachを使った例。ハッシュ(連想配列)に対する反復
while(my($key, $value) = each %hash){
print "$key=$value\n";
}
PHP
PHPではIteratorインターフェイスを実装することにより、任意のイテレータを定義することができ、foreach
、while
といったキーワードでイテレータを簡単に利用することができる。
また、大抵のオブジェクトにはあらかじめイテレータが実装されている。
# foreachを使った例。配列・連想配列・オブジェクト等に対し全く同じように使用できる。
foreach ( $elements as $key=>$value ){
print $key . "=" . $value . " \n";
}
Python
Pythonは次の要素を返す__next__()
メソッドを持つオブジェクトを外部イテレータとして使う。コンテナオブジェクトの__iter__()
メソッドがイテレータを返す。(便宜上、イテレータの__iter__()
は自身を返す)
通常のプログラミングでは、obj.__iter__()
のように直接呼ぶのではなく、組込み関数iterを使ってiter(obj)
のようにする。同様に、通常の用法で呼ぶことを前提とした場合は__next__()
ではなくnext()
を使う。for文(Foreach文)はイテレータが使える場合はイテレータを使うが、そうでないコンテナオブジェクトに対しては直接、__getitem__()
メソッドにより要素を取得し繰返しを実行する。
Pythonのfor文においてはiterableを範囲にとって、暗黙的にiteratorを利用する点を指して内部イテレータと呼ばれる場合もある。
cont = iteratable_container()
# イテレータを直接使う
it = iter(cont)
while 1:
try:
print it.next()
except StopIteration:
# 要素が残っていないならば、
# next()はStopIteration例外を発生させる
break
# for文で使う
for element in cont:
print element
また、Pythonには一種のコルーチンを記述できるジェネレータもある。ジェネレータはイテレータを返す関数で、yield文により、__next__()
で実行される手続きを次々と記述できる。
def fruit_generator():
yield 'banana' # 最初の __next__() によりここまで実行され 'banana' を返す
yield 'apple' # 次の __next__() によりここまで実行され 'apple' を返す
yield 'orange' # 3回目の __next__() によりここまで実行され 'orange' を返す
for fruit in fruit_generator():
print(fruit)
it = fruit_generator()
print(next(it))
print(next(it))
print(next(it))
print(next(it)) # この行で StopIteration 例外になる
Ruby
Rubyでは、Arrayなどのコンテナオブジェクトが、eachなどのイテレートするメソッドを持っている内部イテレータである。メソッド呼出しの直後に { ... }
という書式で「ブロック」を書くと、その中の手続きが繰返し実行される。「ブロック付きメソッド」(あるいは、「ブロック付きメソッド呼び出し」)と言い、これをイテレータとも呼ぶ。
ブロックを付けて呼ばれたメソッド中でyieldを実行すると、ブロックの中の手続きが実行される。Pythonのジェネレータと同じyieldというキーワードで、コードの見た目も似たものになるが、具体的な働きは全く異なることに注意。
class MyObj
def my123
yield 1
yield 2
yield 3
end
end
arr = ["a", "b", "c"]
arr.each do |x|
p x
end
obj = MyObj.new
obj.my123 do |x|
p x
end
.NET言語
C#、VB.NETなどの.NET Frameworkに準拠する.NET言語において、反復子 (iterator) は値の順序付き列を産出 (yield) する文のブロック(狭義ではyield文を含むメソッド)を意味する[3][4][5]。これを反復子ブロック (iterator block) とも呼ぶ[6]。また、コレクションに対する列挙操作を行なう機能を提供するための媒介インターフェイス[7]を列挙子 (enumerator) と呼び、IEnumerator
インターフェイスによって表す。IEnumerator
インターフェイスはMoveNext()
メソッドを定義しており、このメソッドを使用することによりコレクション中の次の要素に進むと同時に、コレクションの末尾に到達するかどうかを判定する。Current
プロパティを使用することによってコレクション内部の要素を取得する。コレクションの最初の要素に戻す方法としてReset()
メソッドを使用する。
列挙子を得るには通例IEnumerable
インターフェイスを実装するオブジェクトのGetEnumerator()
メソッドを呼び出す。一般的にコレクション クラスはこのIEnumerable
インターフェイスを実装する。GetEnumerator()
を明示的に呼び出さず、foreach
文を代わりに使用することもできる(GetEnumerator()
はコンパイラによって暗黙的に呼び出される)。IEnumerator
およびIEnumerable
インターフェイスは、.NET 2.0でジェネリック版 (System.Collections.Generic
) として拡張された。
C# 2.0
// 明示的な使い方
IEnumerator<MyType> iter = list.GetEnumerator();
while (iter.MoveNext())
Console.WriteLine(iter.Current);
// 暗黙的な使い方
foreach (MyType value in list)
Console.WriteLine(value);
C# 2.0およびVB.NET 11は反復子の形でジェネレータ (generator) をサポートする。ジェネレータはIEnumerator
またはIEnumerable
を返すよう宣言されたメソッドであるが、オブジェクトインスタンスを返す代わりに要素のシーケンスを生成するためのyield return
ステートメントを使用する。yieldステートメントを用いて記述されたジェネレータはコンパイラによって、適切なインターフェイスを実装する新しいクラスに変換される。ただし、ジェネレータ(反復子)はIEnumerator.Reset()
メソッドをサポートしない。
// 反復子の記述例。
static IEnumerable<int> MyIteratorMethod() {
yield return 1;
yield return -1;
yield return 0;
yield break;
}
IEnumerable<int> elements = MyIteratorMethod(); // この時点では、まだメソッドの本体は実行されない。
// 列挙によりメソッドの本体が順次「遅延実行」される。
foreach (int element in elements)
Console.WriteLine(element);
Visual Basic 8.0
' 明示的な使い方
Dim iter As IEnumerator(Of MyType) = list.GetEnumerator()
Do While iter.MoveNext()
Console.WriteLine(iter.Current)
Loop
' 暗黙的な使い方
For Each value As MyType In list
Console.WriteLine(value)
Next value
D言語
D言語では、標準ライブラリにレンジ (Range) というイテレータが定義されており、規定されたインターフェイスを持っているオブジェクトなら何でもレンジとして扱うことができる。
foreach (item; range)
writeln(item);
脚注
- ^ JISC 日本工業標準調査会 JISX3014「プログラム言語C++」、JISX3015「プログラム言語C#」など。
- ^ 繰り返し子(くりかえしし)という訳もあるが一般的ではない。「Rubyプログラミング入門」著者: 原信一郎、出版: オーム社、p.197。
- ^ JISX3015「プログラム言語C#」p.64より引用。
- ^ 反復子 (C#) | Microsoft Docs
- ^ yield (C# リファレンス) | Microsoft Docs
- ^ yield (C# リファレンス)
- ^ IEnumerable・IEnumerator - Programming/.NET Framework/列挙操作と列挙子 - 総武ソフトウェア推進所