While, For y Loop
Move ofrece tres construcciones para bucles: while
, for
y loop
.
Bucles while
Sección titulada «Bucles while»La construcción while
repite el cuerpo (una expresión de tipo unit) hasta que la condición (una expresión de tipo bool
) se evalúe como false
.
Aquí hay un ejemplo de un bucle while
simple que calcula la suma de los números del 1
al n
:
script { fun sum(n: u64): u64 { let sum = 0; let i = 1; while (i <= n) { sum = sum + i; i = i + 1 };
sum }}
Los bucles infinitos están permitidos:
script { fun foo() { while (true) { } }}
La expresión break
puede ser usada para salir de un bucle antes de que la condición se evalúe como false
. Por ejemplo, este bucle usa break
para encontrar el factor más pequeño de n
que sea mayor que 1:
script { fun smallest_factor(n: u64): u64 { // asumiendo que la entrada no es 0 o 1 let i = 2; while (i <= n) { if (n % i == 0) break; i = i + 1 };
i }}
La expresión break
no puede ser usada fuera de un bucle.
continue
Sección titulada «continue»La expresión continue
omite el resto del bucle y continúa a la siguiente iteración. Este bucle usa continue
para calcular la suma de 1, 2, ..., n
, excepto cuando el número es divisible por 10:
script { fun sum_intermediate(n: u64): u64 { let sum = 0; let i = 0; while (i < n) { i = i + 1; if (i % 10 == 0) continue; sum = sum + i; };
sum }}
La expresión continue
no puede ser usada fuera de un bucle.
El tipo de break
y continue
Sección titulada «El tipo de break y continue»break
y continue
, al igual que return
y abort
, pueden tener cualquier tipo. Los siguientes ejemplos ilustran dónde este tipado flexible puede ser útil:
script { fun pop_smallest_while_not_equal( v1: vector<u64>, v2: vector<u64>, ): vector<u64> { let result = vector::empty(); while (!vector::is_empty(&v1) && !vector::is_empty(&v2)) { let u1 = *vector::borrow(&v1, vector::length(&v1) - 1); let u2 = *vector::borrow(&v2, vector::length(&v2) - 1); let popped = if (u1 < u2) vector::pop_back(&mut v1) else if (u2 < u1) vector::pop_back(&mut v2) else break; // Aquí, `break` tiene tipo `u64` vector::push_back(&mut result, popped); };
result }}
Bucles for
Sección titulada «Bucles for»Desde la Versión del Lenguaje 2.0
Los bucles for
proporcionan una forma más concisa de iterar sobre rangos o colecciones:
Iteración sobre Rangos
Sección titulada «Iteración sobre Rangos»script { fun sum_range(start: u64, end: u64): u64 { let sum = 0; for (i in start..end) { sum = sum + i; }; sum }}
Iteración sobre Vectores
Sección titulada «Iteración sobre Vectores»script { fun sum_vector(numbers: &vector<u64>): u64 { let sum = 0; for (i in 0..vector::length(numbers)) { sum = sum + *vector::borrow(numbers, i); }; sum }}
Con Referencias
Sección titulada «Con Referencias»script { fun increment_all(numbers: &mut vector<u64>) { for (i in 0..vector::length(numbers)) { let element = vector::borrow_mut(numbers, i); *element = *element + 1; }; }}
Bucles loop
Sección titulada «Bucles loop»Un bucle loop
es un bucle infinito que debe ser terminado explícitamente con break
:
script { fun search_value(values: &vector<u64>, target: u64): u64 { let i = 0; loop { if (i >= vector::length(values)) { abort 1; // valor no encontrado };
if (*vector::borrow(values, i) == target) { break i; // devuelve el índice };
i = i + 1; } }}
break
con Valores
Sección titulada «break con Valores»Los bucles loop
pueden devolver valores usando break
:
script { fun find_first_even(numbers: &vector<u64>): u64 { let i = 0; loop { if (i >= vector::length(numbers)) { break 0; // valor por defecto si no se encuentra };
let value = *vector::borrow(numbers, i); if (value % 2 == 0) { break value; // devuelve el primer número par };
i = i + 1; } }}
Etiquetas de Bucle
Sección titulada «Etiquetas de Bucle»Para bucles anidados, puedes usar etiquetas para especificar qué bucle romper o continuar:
script { fun nested_search(matrix: &vector<vector<u64>>, target: u64): (u64, u64) { let i = 0; 'outer: loop { if (i >= vector::length(matrix)) { break 'outer (0, 0); // no encontrado };
let row = vector::borrow(matrix, i); let j = 0; 'inner: loop { if (j >= vector::length(row)) { break 'inner; };
if (*vector::borrow(row, j) == target) { break 'outer (i, j); // encontrado en (i, j) };
j = j + 1; };
i = i + 1; } }}
Ejemplos Prácticos
Sección titulada «Ejemplos Prácticos»Búsqueda en Vector
Sección titulada «Búsqueda en Vector»public fun find_index(values: &vector<u64>, target: u64): u64 { let i = 0; while (i < vector::length(values)) { if (*vector::borrow(values, i) == target) { return i; }; i = i + 1; }; abort E_NOT_FOUND}
Validación de Todos los Elementos
Sección titulada «Validación de Todos los Elementos»public fun all_positive(numbers: &vector<u64>): bool { let i = 0; while (i < vector::length(numbers)) { if (*vector::borrow(numbers, i) == 0) { return false; }; i = i + 1; }; true}
Procesamiento con Acumulador
Sección titulada «Procesamiento con Acumulador»public fun calculate_total_with_discount( prices: &vector<u64>, discount_rate: u64): u64 { let total = 0; let i = 0;
while (i < vector::length(prices)) { let price = *vector::borrow(prices, i); let discounted = price * (100 - discount_rate) / 100; total = total + discounted; i = i + 1; };
total}
Filtrado y Transformación
Sección titulada «Filtrado y Transformación»public fun filter_and_double_evens(numbers: &vector<u64>): vector<u64> { let result = vector::empty<u64>(); let i = 0;
while (i < vector::length(numbers)) { let num = *vector::borrow(numbers, i); if (num % 2 == 0) { vector::push_back(&mut result, num * 2); }; i = i + 1; };
result}
Búsqueda con Condición Compleja
Sección titulada «Búsqueda con Condición Compleja»public fun find_user_by_criteria( users: &vector<User>, min_age: u8, required_status: u8): u64 { let i = 0; loop { if (i >= vector::length(users)) { abort E_USER_NOT_FOUND; };
let user = vector::borrow(users, i); if (user.age >= min_age && user.status == required_status) { break i; };
i = i + 1; }}
Procesamiento por Lotes
Sección titulada «Procesamiento por Lotes»public fun process_in_batches( items: &mut vector<Item>, batch_size: u64) { let i = 0; while (i < vector::length(items)) { let batch_end = if (i + batch_size < vector::length(items)) { i + batch_size } else { vector::length(items) };
// Procesar lote actual let j = i; while (j < batch_end) { let item = vector::borrow_mut(items, j); process_item(item); j = j + 1; };
i = batch_end; }}
Buenas Prácticas
Sección titulada «Buenas Prácticas»1. Elige el Tipo de Bucle Correcto
Sección titulada «1. Elige el Tipo de Bucle Correcto»// ✓ Bueno: for loop para iteraciones simplesfor (i in 0..length) { process(i);}
// ✓ Bueno: while loop para condiciones complejaswhile (has_more_data() && !error_occurred()) { process_data();}
// ✓ Bueno: loop para bucles infinitos controladosloop { let input = get_next_input(); if (input == EXIT_CODE) break; process(input);}
2. Evita Bucles Infinitos Accidentales
Sección titulada «2. Evita Bucles Infinitos Accidentales»// ✓ Bueno: asegúrate de que la condición eventualmente sea falsalet mut i = 0;while (i < limit) { // ... hacer trabajo ... i = i + 1; // importante: actualizar la variable de control}
// ✗ Peligroso: posible bucle infinitolet mut i = 0;while (i < limit) { // ... hacer trabajo ... // ¡olvidé incrementar i!}
3. Usa break
y continue
Estratégicamente
Sección titulada «3. Usa break y continue Estratégicamente»// ✓ Bueno: early exit para eficienciafor (i in 0..vector::length(items)) { let item = vector::borrow(items, i); if (item.is_target()) { break; // encontrado, no necesitamos seguir buscando }}
// ✓ Bueno: skip processing para casos especialesfor (i in 0..vector::length(items)) { let item = vector::borrow(items, i); if (item.should_skip()) { continue; // skip este item } process_item(item);}
4. Limita el Alcance de Variables de Bucle
Sección titulada «4. Limita el Alcance de Variables de Bucle»// ✓ Bueno: variable limitada al bucle{ let mut sum = 0; for (i in 0..count) { sum = sum + calculate_value(i); } // sum sale de alcance aquí}
// usar sum aquí causaría error de compilación
5. Maneja Casos Límite
Sección titulada «5. Maneja Casos Límite»// ✓ Bueno: verifica vectores vacíospublic fun safe_process(items: &vector<Item>) { if (vector::is_empty(items)) { return; // maneja el caso vacío }
let i = 0; while (i < vector::length(items)) { // proceso seguro i = i + 1; }}
6. Usa Constantes para Límites
Sección titulada «6. Usa Constantes para Límites»const MAX_ITERATIONS: u64 = 1000;const BATCH_SIZE: u64 = 100;
public fun bounded_loop() { let mut iterations = 0; loop { if (iterations >= MAX_ITERATIONS) { abort E_TOO_MANY_ITERATIONS; }
// ... hacer trabajo ... iterations = iterations + 1;
if (done()) break; }}