Se um foreach
for encontrado pelo compilador
foreach (element; range)
{
// corpo do Loop...
}
ele é reescrito internamente de forma semelhante à seguinte:
for (auto __rangeCopy = range;
!__rangeCopy.empty;
__rangeCopy.popFront())
{
auto element = __rangeCopy.front;
// Loop body...
}
Qualquer objeto que atenda à seguinte interface é chamado de range
(ou, mais especificamente, InputRange
) e, portanto, é um tipo que pode ser iterado:
interface InputRange(E)
{
bool empty();
E front();
void popFront();
}
Dê uma olhada no exemplo à direita para inspecionar a implementação e o uso de um intervalo de entrada mais próximo.
Os ranges são lazy. Eles não serão avaliados até que sejam solicitados. Portanto, um range de um range infinito pode ser obtido:
42.repeat.take(3).writeln; // [42, 42, 42]
Se o objeto range for um tipo de valor, então o range será copiado e somente a cópia será utilizada:
auto r = 5.iota;
r.drop(5).writeln; // []
r.writeln; // [0, 1, 2, 3, 4]
Se o objeto range for um tipo de referência (por exemplo, class
ou std.range.refRange
),
então o range será utilizado e não será redefinido:
auto r = 5.iota;
auto r2 = refRange(&r);
r2.drop(5).writeln; // []
r2.writeln; // []
InputRanges
copiáveis são ForwardRanges
A maioria dos ranges na biblioteca padrão são structs e, portanto, a iteração foreach
geralmente não é destrutiva, embora não seja garantida. Se essa
garantia for importante, uma especialização de um InputRange
pode ser usada
forward ranges com um método .save
:
interface ForwardRange(E) : InputRange!E
{
typeof(this) save();
}
// by value (Structs)
auto r = 5.iota;
auto r2 = refRange(&r);
r2.save.drop(5).writeln; // []
r2.writeln; // [0, 1, 2, 3, 4]
ForwardRanges
pode ser estendido por ranges bidirecionais + ranges de acesso aleatórioHá duas extensões do ForwardRange
copiável: (1) um range bidirecional
e (2) um range de acesso aleatório.
Um range bidirecional permite a iteração de trás para frente:
interface BidirectionalRange(E) : ForwardRange!E
{
E back();
void popBack();
}
5.iota.retro.writeln; // [4, 3, 2, 1, 0]
Um range de acesso aleatório tem um length
(comprimento) conhecido e cada elemento pode ser acessado diretamente.
interface RandomAccessRange(E) : ForwardRange!E
{
E opIndex(size_t i);
size_t length();
}
Em termos práticos, o range de acesso aleatório mais conhecido é o array de D:
auto r = [4, 5, 6];
r[1].writeln; // 5
As funções em std.range
e
std.algorithm
fornecem
blocos de construção que fazem uso dessa interface. Os ranges permitem a
composição de algoritmos complexos por trás de um objeto que
pode ser iterado com facilidade. Além disso, os ranges permitem a criação de objetos lazy
que só executam um cálculo quando ele é realmente necessário
em uma iteração, por exemplo, quando o próximo elemento do range é acessado.
Algoritmos especiais do range serão apresentados mais adiante na seção
D's Gems.