⬅️ Volver a la clase 065 · 🧬 Primos del Atlas · 🧟 Índice de lenguajes vivos · 📚 Índice
Sumar una lista de enteros cuya longitud no se sabe de antemano. Ahí está la diferencia con la clase anterior: un bucle de rango necesita saber cuántas vueltas dará; recorrer una colección necesita otra cosa — preguntarle a la propia colección si queda algo. Esa inversión es el concepto del iterador.
🎯 Estos lenguajes no están aquí por ser antiguos
El criterio es doble y se declara en la ficha de cada uno: que se ejecute hoy —banca, sanidad, aviónica, ERP, diseño de chips, CAD— y que deje a la vista un concepto que los diez del núcleo esconden.
Aquí el concepto es el recorrido de una colección, y estos lenguajes lo enseñan porque muestran lo que había antes de que existiera el iterador como abstracción. En COBOL y Fortran clásico hay que llevar el contador a mano, y por eso el idioma es "una tabla y una variable con cuántos elementos tiene ocupados". En RPG, el bucle ni siquiera se escribe: lo pone el ciclo del programa.
Y M enseña la versión más potente y menos conocida:
$orderrecorre un árbol persistente de un millón de nodos sin cargarlo en memoria, que es exactamente lo que hoy llamamos un iterador perezoso sobre una base de datos.Y ninguno es una foto fija: casi todos han incorporado en los últimos años JSON, REST, GPU, Unicode o Git. Cada ficha lo detalla en su sección
🔄 Lo que se ha modernizado.
suma=<suma de todos>suma = Σ elementos| stdin | esperado |
|---|---|
3 1 4 |
suma=8 |
10 20 30 |
suma=60 |
5 |
suma=5 |
Qué está verificado en esta página. Los lenguajes de la sección 🟢 se ejecutan en CI contra este mismo
casos.json, igual que las diez implementaciones del núcleo (workflow Labs). Los de la sección 🟡 no pueden cumplir este contrato sin falsear el lenguaje, y se explica por qué. Los de la sección ⚪ sí podrían, pero su cadena de herramientas no está en los runners: son correctos, sin sello de máquina.
Ficha completa · Banca, seguros, gobierno, medios de pago · cobc -x -free prog.cob
IDENTIFICATION DIVISION.
PROGRAM-ID. SUMALISTA.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 LINEA PIC X(200).
01 I PIC 9(4) COMP-3.
01 LARGO PIC 9(4) COMP-3.
01 TOKEN PIC X(20).
01 TLEN PIC 9(4) COMP-3.
01 C PIC X.
01 TOTAL PIC S9(18) COMP-3.
01 ED-T PIC -(17)9.
PROCEDURE DIVISION.
ACCEPT LINEA
MOVE 0 TO TOTAL
MOVE SPACES TO TOKEN
MOVE 0 TO TLEN
COMPUTE LARGO = FUNCTION LENGTH(FUNCTION TRIM(LINEA)) + 1
PERFORM VARYING I FROM 1 BY 1 UNTIL I > LARGO
MOVE LINEA(I:1) TO C
IF C = SPACE
IF TLEN > 0
COMPUTE TOTAL = TOTAL + FUNCTION NUMVAL(TOKEN(1:TLEN))
MOVE SPACES TO TOKEN
MOVE 0 TO TLEN
END-IF
ELSE
ADD 1 TO TLEN
MOVE C TO TOKEN(TLEN:1)
END-IF
END-PERFORM
MOVE TOTAL TO ED-T
DISPLAY "suma=" FUNCTION TRIM(ED-T)
STOP RUN.
Lo que esta clase enseña en COBOL. COBOL no tiene iteradores ni colecciones de tamaño
variable. Tiene tablas con OCCURS, que son arrays de tamaño fijo declarado, y el número de
elementos ocupados lo lleva el programador en una variable aparte:
01 TABLA-VENTAS.
05 NUM-VENTAS PIC 9(4) COMP-3.
05 VENTA OCCURS 1 TO 1000 TIMES
DEPENDING ON NUM-VENTAS
PIC S9(9)V99 COMP-3.
PERFORM VARYING I FROM 1 BY 1 UNTIL I > NUM-VENTAS
ADD VENTA(I) TO TOTAL
END-PERFORM
OCCURS DEPENDING ON es lo más cerca que llega COBOL de una colección de tamaño variable: la tabla
declara un máximo y una variable dice cuántos elementos son válidos ahora. Es exactamente el
patrón "array más contador" que C usa, con la ventaja de que aquí la relación está declarada y el
compilador puede comprobar los límites con SSRANGE.
Y COBOL tiene una operación sobre tablas que sí es de alto nivel y conviene conocer:
SEARCH VENTA VARYING I
AT END DISPLAY "no encontrado"
WHEN VENTA(I) > LIMITE DISPLAY "encontrado en " I
END-SEARCH
SEARCH ALL VENTA *> búsqueda BINARIA, si la tabla está ordenada
WHEN CLAVE(I) = BUSCADA ...
SEARCH ALL hace una búsqueda binaria sobre una tabla declarada con ASCENDING KEY. Es un algoritmo
completo dentro del lenguaje, en 1968.
Ficha completa · HPC, clima, física, BLAS/LAPACK · gfortran -O2 prog.f90
program suma_lista
implicit none
character(len=1000) :: linea
integer :: valores(200), n, i, total, ios
read(*, '(A)') linea
! Cuántos valores hay en la línea: se prueba a leer 1, 2, 3... hasta fallar.
n = 0
do i = 1, 200
read(linea, *, iostat=ios) valores(1:i)
if (ios /= 0) exit
n = i
end do
total = 0
do i = 1, n
total = total + valores(i)
end do
write(*, '(A,I0)') 'suma=', total
end program suma_lista
Lo que esta clase enseña en Fortran. El bucle que cuenta los valores es el idioma clásico de
Fortran para "no sé cuántos hay en esta línea", y merece explicación porque es raro visto desde
fuera: se intenta leer 1 valor, luego 2, luego 3, y se para cuando la lectura falla. iostat=ios
captura el error en lugar de abortar.
Es O(n²) y nadie lo defendería hoy, y sin embargo aparece en código científico real, porque el sistema de E/S de Fortran está diseñado para registros de forma conocida, no para líneas de longitud arbitraria. Cuando el fichero tiene una cabecera que dice cuántos valores vienen —que es lo normal en los formatos científicos— el problema no existe.
Lo que Fortran sí tiene, y es su gran aportación a esta clase, es que el bucle sobre la colección casi nunca hace falta:
total = sum(valores(1:n)) ! sin bucle
media = sum(v) / size(v)
mayor = maxval(v)
donde = maxloc(v) ! la POSICIÓN del máximo
cuantos = count(v > 0) ! cuántos cumplen
todos = all(v > 0) ! ¿todos?
alguno = any(v < 0) ! ¿alguno?
sum, product, maxval, minval, maxloc, count, all, any, dot_product, matmul —
todas operan sobre arrays completos y todas se vectorizan. En Fortran, recorrer un array a mano es
casi siempre un síntoma de que no conoces la intrínseca que hace eso.
Ficha completa · Aviónica, espacio, ferrocarril, defensa · gnatmake prog.adb
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Suma_Lista is
Linea : String (1 .. 500);
Ultimo : Natural;
Pos : Positive := 1;
Fin : Positive;
Valor : Integer;
Total : Integer := 0;
begin
Get_Line (Linea, Ultimo);
while Pos <= Ultimo loop
-- Get sobre una PORCIÓN de cadena: devuelve el valor y hasta dónde leyó.
Get (Linea (Pos .. Ultimo), Valor, Fin);
Total := Total + Valor;
Pos := Fin + 1;
end loop;
Put ("suma="); Put (Total, Width => 1); New_Line;
end Suma_Lista;
Lo que esta clase enseña en Ada. Get (From : String; Item : out Integer; Last : out Positive)
es una forma de lectura que casi ningún lenguaje ofrece: analiza un valor a partir de una cadena y
devuelve hasta dónde ha llegado. Con eso, recorrer una línea de valores es avanzar Pos hasta el
final, sin partir la cadena ni reservar memoria.
Es el mismo mecanismo que std::from_chars de C++17, disponible desde 1983.
Pero la aportación de Ada a esta clase llegó con Ada 2012 y sus interfaces de iterador, que
permiten que cualquier tipo tuyo se recorra con for ... of:
for Elemento of Mi_Coleccion loop ... end loop;
Para que eso funcione, el tipo declara el aspecto Iterable o implementa Ada.Iterator_Interfaces,
con First, Next y Has_Element. Es exactamente el mismo contrato que IEnumerable en C#,
Iterator en Java o begin/end en C++.
Y Ada 2022 fue más lejos con las expresiones de agregado iteradas, que son comprensiones de listas:
Cuadrados : constant Vector := [for I in 1 .. 10 => I * I];
Un lenguaje de 1983 incorporando comprensiones en 2022 es, otra vez, el argumento de esta sección entera: no son fotos fijas.
Ficha completa · Escritorio empresarial, TPV, industria (vía Delphi y Free Pascal) · fpc -Mobjfpc prog.pas
program SumaLista;
{$MODE OBJFPC}{$H+}
uses SysUtils;
var
Linea, Token: string;
I, Total: Integer;
begin
ReadLn(Linea);
Linea := Trim(Linea) + ' ';
Total := 0;
Token := '';
for I := 1 to Length(Linea) do
begin
if Linea[I] = ' ' then
begin
if Token <> '' then
begin
Total := Total + StrToInt(Token);
Token := '';
end;
end
else
Token := Token + Linea[I];
end;
WriteLn('suma=', IntToStr(Total));
end.
Lo que esta clase enseña en Pascal. El Pascal ISO no tiene función de partir cadenas, así que el tokenizador se escribe a mano. Es una carencia real y muy visible en cuanto se procesa texto.
Free Pascal y Delphi lo resolvieron por dos vías distintas, y conviene conocer las dos porque aparecen en código de épocas diferentes:
{ 1) TStringList con delimitador: la forma clásica de Delphi }
Lista := TStringList.Create;
try
Lista.Delimiter := ' ';
Lista.DelimitedText := Linea;
for I := 0 to Lista.Count - 1 do
Total := Total + StrToInt(Lista[I]);
finally
Lista.Free;
end;
{ 2) Ayudantes de tipo, en Delphi moderno }
for S in Linea.Split([' ']) do ...
Fíjate en el try..finally de la primera: TStringList es un objeto y hay que liberarlo, con la
disciplina de la clase 042. Ese bloque es el idioma más repetido del código Delphi del mundo.
Y for..in sobre colecciones llegó a Free Pascal y Delphi en 2005, con el mismo contrato que los
demás: el tipo implementa GetEnumerator y devuelve un objeto con MoveNext y Current. Es el
patrón que C# había estrenado poco antes, y que Ada, C++ y Java tienen con otros nombres. Los cinco
llegaron a la misma solución.
Ficha completa · IA simbólica, CAD, investigación · sbcl --script prog.lisp
(let ((total 0))
(loop for valor = (read *standard-input* nil :fin)
until (eq valor :fin)
do (incf total valor))
(format t "suma=~D~%" total))
Lo que esta clase enseña en Common Lisp. (read stream nil :fin) es la lectura con valor
centinela: en lugar de señalar un error al llegar al final, devuelve el objeto que le indiques.
Aquí, la palabra clave :fin, que no puede confundirse con ningún número.
Es el mismo patrón que iostat en Fortran y que comprobar el flujo en C++, y es la primera pieza de
esta clase: cómo sabe el bucle que ya no hay más.
La segunda pieza es que Lisp tiene funciones de secuencia genéricas que funcionan igual sobre listas, vectores y cadenas —lo que ya se vio en la clase 048—:
(reduce #'+ lista) ; suma
(count-if #'evenp lista) ; cuántos pares
(find-if (lambda (x) (> x 100)) lista)
(position 42 lista)
(some #'minusp lista) ; ¿alguno negativo? cortocircuita
(every #'plusp lista)
(map 'vector #'1+ lista) ; el TIPO del resultado como argumento
some y every cortocircuitan, al contrario que any/all de PL/I sobre cadenas de bits.
Y loop tiene cláusulas de acumulación que hacen innecesaria la variable manual de este programa:
(loop for x in lista sum x)
(loop for x in lista when (evenp x) collect x)
(loop for x in lista maximize x)
(loop for x in lista count (plusp x))
sum, collect, maximize, minimize, count, append y nconc son parte del mini-lenguaje de
loop, y cubren casi todo lo que en otros lenguajes exige map/filter/reduce.
Ficha completa · Diseño de chips (EDA), redes, testing · tclsh prog.tcl
gets stdin linea
set total 0
foreach v [split [string trim $linea]] {
incr total $v
}
puts "suma=$total"
Lo que esta clase enseña en Tcl. foreach es el recorrido de colección de Tcl, y tiene dos
formas que casi ningún lenguaje ofrece:
foreach {clave valor} $lista { ... } ;# de DOS en dos
foreach a $lista1 b $lista2 { ... } ;# dos listas EN PARALELO
foreach a $l1 {b c} $l2 { ... } ;# ¡y con tamaños de grupo distintos!
Recorrer una lista de pares clave-valor de dos en dos, o dos listas a la vez, son operaciones que en
C++ exigen iteradores manuales y en Python el zip. Aquí son argumentos adicionales del mismo
comando.
Y split es la contrapartida de join: convierte una cadena en lista partiendo por los caracteres
que indiques. Sin argumento, parte por espacios en blanco, que es lo que hace este programa.
Un aviso que conviene tener presente: split con un delimitador explícito produce elementos
vacíos si hay separadores consecutivos:
split "a,,b" "," ;# -> {a {} b} -- tres elementos, uno vacío
split "a b" ;# -> {a {} b} -- ¡también!
split [string trim "a b"] " " ;# sigue dando el vacío
Por eso este programa usa split sin argumento sobre la cadena recortada, que colapsa las rachas
de espacios. Es la misma diferencia que en COBOL entre DELIMITED BY SPACE y DELIMITED BY ALL
SPACES, y en Perl entre split /,/ y split ' '.
Ficha completa · Sysadmin, texto, bioinformática · perl prog.pl
use strict;
use warnings;
use List::Util qw(sum0);
my $linea = <STDIN>;
chomp $linea;
my $total = sum0(split ' ', $linea);
print "suma=$total\n";
Lo que esta clase enseña en Perl. sum0(split ' ', $linea) es toda la solución: partir y sumar,
sin bucle y sin variable acumuladora.
List::Util está en el núcleo desde 2001 y sus funciones están escritas en C:
use List::Util qw(sum sum0 max min first any all none reduce shuffle uniq);
sum @v # undef si la lista está vacía
sum0 @v # 0 si está vacía <- casi siempre lo que quieres
first { $_ > 100 } @v # cortocircuita
reduce { $a + $b } @v # el plegado general
La distinción entre sum y sum0 es la clase 053 aplicada aquí: "no hay elementos" y "la suma es
cero" son cosas distintas, y Perl te deja elegir cuál devuelve.
Y el recorrido idiomático es foreach, con la propiedad que ya apareció en la clase 064 y que
conviene recordar porque es peligrosa y útil a partes iguales:
for my $x (@lista) { $x *= 2 } # MODIFICA @lista: $x es un ALIAS
for my $x (@lista) { my $y = $x * 2 } # sin tocar el original
La variable del bucle no es una copia, es una referencia al elemento real. En Python o Java eso
no ocurre. Ada lo hace igual con for E of cuando el elemento no es constante, y C++ solo si escribes
auto&.
Ficha completa · Videojuegos, navegadores, finanzas, HPC · g++ -std=c++17 prog.cpp
#include <iostream>
#include <iterator>
#include <numeric>
#include <sstream>
#include <string>
#include <vector>
int main() {
std::string linea;
if (!std::getline(std::cin, linea)) return 1;
std::istringstream iss(linea);
const std::vector<int> valores{std::istream_iterator<int>(iss),
std::istream_iterator<int>()};
const int total = std::accumulate(valores.begin(), valores.end(), 0);
std::cout << "suma=" << total << '\n';
return 0;
}
Lo que esta clase enseña en C++. Este programa construye el vector directamente desde un par de
iteradores de flujo, sin bucle de lectura. std::istream_iterator<int>(iss) es un iterador que, al
avanzar, lee el siguiente entero; el construido sin argumentos representa el final.
Ese es el diseño de la STL de Alexander Stepanov, y es la aportación conceptual de C++ a esta clase: los algoritmos no conocen los contenedores, conocen iteradores.
std::accumulate(v.begin(), v.end(), 0); // sobre un vector
std::accumulate(l.begin(), l.end(), 0); // sobre una lista enlazada
std::accumulate(s.begin(), s.end(), 0); // sobre un conjunto
std::accumulate(it_flujo, it_fin, 0); // ¡sobre un FLUJO de entrada!
El mismo algoritmo, sin recompilar nada distinto, sobre cuatro estructuras que no se parecen en
nada. Un contenedor solo tiene que ofrecer iteradores; un algoritmo solo tiene que pedirlos. Esa
separación en tres piezas —contenedores, iteradores, algoritmos— es la idea que después copiaron los
Stream de Java, los IEnumerable de C# y los Iterator de Rust.
C++20 la refinó con los rangos, que evitan tener que escribir begin() y end():
#include <ranges>
const int total = std::ranges::fold_left(valores, 0, std::plus{});
Estos lenguajes no pueden leer de stdin y escribir en stdout sin dejar de ser ellos
mismos. No es una limitación del material: es su naturaleza. El cálculo es el mismo y la forma
de entrar y salir es la de su anfitrión. No pasan por el verificador, y se dice.
Ficha completa · IBM i: ERP, retail, logística, manufactura · CRTBNDRPG sobre IBM i
En IBM i un programa recibe sus datos por parámetros, por un fichero o por una pantalla, nunca por la entrada estándar.
**free
ctl-opt dftactgrp(*no) actgrp(*caller);
dcl-pi SUMALIS;
linea varchar(200) const;
end-pi;
dcl-s trozos varchar(20) dim(50);
dcl-s cuantos int(10);
dcl-s i int(10);
dcl-s total int(20) inz(0);
dcl-s salida char(40);
trozos = %split(linea : ' ');
cuantos = %elem(trozos);
for i = 1 to cuantos;
total += %int(trozos(i));
endfor;
salida = 'suma=' + %char(total);
dsply salida;
*inlr = *on;
return;
Lo que esta clase enseña en RPG. %split —que parte una cadena y devuelve una matriz— se
añadió en IBM i 7.4, en 2019. Durante cincuenta años, partir una cadena en RPG se hacía con un
bucle y %scan. Es otro recordatorio de que estos lenguajes siguen recibiendo funcionalidad.
Pero el recorrido de colecciones de verdad en RPG no es sobre matrices: es sobre ficheros de base de datos, y ahí está la aportación de esta clase.
dcl-f CLIENTES usage(*input) keyed;
setll *loval CLIENTES; // posicionarse al principio
read CLIENTES;
dow not %eof(CLIENTES);
total += CLI_SALDO; // los campos son VARIABLES del programa
read CLIENTES;
enddo;
setll (set lower limit), read, reade (leer mientras la clave coincida), chain (búsqueda
directa) y %eof son el vocabulario de recorrido, y operan sobre millones de registros sin
cargarlos en memoria. Es un iterador perezoso sobre una base de datos, integrado en el lenguaje.
Y en el RPG clásico ni siquiera eso se escribía: con un fichero declarado como entrada primaria,
el ciclo del programa hacía el read y el dow por ti. Tú solo escribías qué hacer con cada
registro. Es exactamente la inversión de control de la clase 063.
Sí podrían cumplir el contrato, pero su cadena de herramientas no está en los runners de CI.
Ficha completa · Mainframe z/OS: banca, seguros · IBM Enterprise PL/I for z/OS
suma_lista: procedure options(main);
declare linea character(200) varying;
declare trozo character(20) varying;
declare total fixed binary(31) initial(0);
declare (i, p) fixed binary(31);
get edit (linea) (a(200));
linea = trim(linea) || ' ';
p = 1;
do i = 1 to length(linea);
if substr(linea, i, 1) = ' ' then do;
if i > p then do;
trozo = substr(linea, p, i - p);
total = total + trozo; /* conversión implícita texto->número */
end;
p = i + 1;
end;
end;
put skip list ('suma=' || trim(char(total)));
end suma_lista;
Lo que esta clase enseña en PL/I. Fíjate en total = total + trozo: trozo es una cadena de
caracteres y se suma a un entero sin ninguna conversión escrita. Es la conversión implícita de la
clase 050, aquí en su versión más cómoda y más peligrosa: funciona perfectamente hasta que el texto
no es un número, y entonces levanta la condición CONVERSION.
Sobre el recorrido de colecciones, PL/I tiene arrays de primera clase con operaciones sobre el conjunto, igual que Fortran:
declare v(100) fixed binary(31);
total = sum(v); /* sin bucle */
mayor = max(v);
v = v * 2; /* opera sobre TODO el array */
v(10:20) = 0; /* una PORCIÓN */
if any(v > 100) then ...
Que un lenguaje de negocio de 1964 tuviera aritmética de arrays completos es notable, y es la influencia directa de FORTRAN en su diseño — recuerda que PL/I nació para unir los dos mundos.
Lo que no tiene es un iterador abstracto: no hay forma de recorrer una estructura definida por el usuario con una construcción genérica. Para eso hay que esperar a los lenguajes con tipos abstractos de datos, que es lo que trajo Ada en 1983 y CLU un poco antes.
Ficha completa · Sanidad: historia clínica, VistA, Epic · YottaDB
SUMA ; Suma de una lista -- clase 065
read linea
set total = 0
for i = 1:1:$length(linea, " ") do
. set total = total + $piece(linea, " ", i)
write "suma=", total, !
quit
Lo que esta clase enseña en M. $length(linea, " ") cuenta cuántos trozos hay, y $piece
extrae el i-ésimo. Es el recorrido de la "colección ligera" de M: una cadena con delimitadores.
Pero la aportación real de M a esta clase es $order, que recorre un árbol de la base de datos
sin cargarlo en memoria:
set clave = ""
for set clave = $order(^VENTAS(clave)) quit:clave = "" do
. set total = total + ^VENTAS(clave)
$order(^VENTAS(clave)) devuelve la siguiente clave existente en orden, y la cadena vacía cuando
no queda ninguna. Con eso se recorre un global de diez millones de nodos consumiendo memoria
constante, porque cada llamada es una búsqueda en el índice del árbol B en disco.
Eso es un iterador perezoso sobre una base de datos, con una sola función y sin cursor que abrir ni cerrar. Y funciona en cualquier nivel de subíndice y en cualquier dirección:
set fecha = $order(^CITAS(pac, fecha), -1) ; la ANTERIOR: recorrido inverso
set sig = $query(^CITAS(pac)) ; el siguiente nodo con VALOR, a cualquier profundidad
$query recorre el árbol entero en profundidad, saltando de nodo a nodo con independencia del número
de subíndices. Es el recorrido completo de una estructura jerárquica en una llamada — y es la razón
de que un sistema clínico en M pueda listar el historial de un paciente sin ningún ORM.
Ficha completa · Banca, seguros, trading · Pharo
| total |
total := (stdin nextLine substrings collect: [ :cada | cada asNumber ])
inject: 0 into: [ :acc :cada | acc + cada ].
Transcript show: 'suma=', total printString; cr.
Lo que esta clase enseña en Smalltalk. inject:into: es el plegado —el reduce de la clase
068— y aquí resuelve la suma sin bucle ni acumulador visible: 0 es el valor inicial y el bloque
recibe el acumulado y el elemento.
Pero lo que hay que ver en esta clase es de dónde sale: inject:into: está implementado en
Collection, en términos de do:, y do: es lo único que una colección tiene que saber hacer:
Collection >> inject: valorInicial into: unBloque
| acumulado |
acumulado := valorInicial.
self do: [ :cada | acumulado := unBloque value: acumulado value: cada ].
^ acumulado
Y con do: implementado, una subclase hereda gratis todo el protocolo: collect:, select:,
reject:, detect:, inject:into:, anySatisfy:, allSatisfy:, sum, max, sorted,
asOrderedCollection, groupedBy:, count:… más de cien métodos.
Ese es el patrón del método plantilla en su forma más pura: define una operación primitiva y
recibe el resto. Es la misma economía que la STL de C++ obtiene con iteradores y que Rust obtiene con
el trait Iterator y sus métodos por defecto — tres soluciones distintas al mismo problema de
diseño, y esta es la más antigua.
Para colecciones grandes o infinitas, Pharo tiene además ReadStream y Generator, que se ven en la
clase siguiente.
Lo transferible es la pregunta "¿quién sabe cuándo termina el recorrido?". Si la respuesta es "una
variable contador que llevo yo", el código es frágil: basta con que alguien inserte un elemento sin
actualizarla. Si la respuesta es "la colección", el recorrido es correcto por construcción. Toda la
evolución que va del DO 100 I = 1, N de Fortran al for x of coleccion de Ada 2012 y al
for (auto& x : v) de C++11 es el traslado de esa responsabilidad del programador a la estructura.
⏮️ Volver a la clase 065 · 🧬 Los primos del Atlas · 🧟 Índice de lenguajes vivos