Diferencia entre revisiones de «Flutter DART Funciones»
| (No se muestran 9 ediciones intermedias del mismo usuario) | |||
| Línea 1: | Línea 1: | ||
==Introducción== | ==Introducción== | ||
| − | * | + | * En Dart, las funciones permiten agrupar código que puede ejecutarse desde diferentes partes de un programa. |
| − | : Como | + | : Como este curso está orientado a alumnado que ya sabe programar en algún lenguaje, no profundizaremos en los conceptos generales de las funciones, sino en las características específicas que ofrece Dart. |
| + | |||
| + | * Es recomendable indicar el tipo de los parámetros que recibe una función y el tipo del dato que devuelve. | ||
* Veamos un ejemplo de función con parámetros: | * Veamos un ejemplo de función con parámetros: | ||
| − | :<syntaxhighlight lang=" | + | |
| − | double sumarValores(double val1, double val2){ | + | :<syntaxhighlight lang="dart" enclose="div" highlight="1-3"> |
| − | + | double sumarValores(double val1, double val2) { | |
| + | return val1 + val2; | ||
} | } | ||
void main() { | void main() { | ||
| − | + | final num1 = 10.32; | |
| − | + | final num2 = 1.35; | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| + | print(sumarValores(num1, num2)); | ||
| + | } </syntaxhighlight> | ||
<br /> | <br /> | ||
| + | |||
==Paso de parámetros== | ==Paso de parámetros== | ||
| − | * Al igual que otros lenguajes podemos pasar datos a las funciones empleando | + | * Al igual que en otros lenguajes, podemos pasar datos a las funciones empleando parámetros. |
| − | : Como ya vimos, | + | |
| + | : Como ya vimos anteriormente, podemos definir parámetros que admitan valores nulos utilizando los tipos ''nullable'', como por ejemplo <code>String?</code>, <code>int?</code> o <code>double?</code>. | ||
<br /> | <br /> | ||
| − | === | + | ===Paso por valor=== |
| + | |||
| + | * En Dart, los argumentos de una función se pasan '''por valor'''. | ||
| + | |||
| + | : Las variables de Dart contienen referencias a objetos. Cuando enviamos una variable como argumento de una función, se copia el valor almacenado en dicha variable. | ||
| + | |||
| + | : Si dicho valor es una referencia a un objeto, se copia la referencia. Por tanto, tanto la variable original como el parámetro pueden hacer referencia al mismo objeto. | ||
| + | |||
| + | : Esto provoca dos comportamientos que debemos diferenciar: | ||
| + | |||
| + | :* Si reasignamos el parámetro dentro de la función, no modificamos la variable utilizada en la llamada. | ||
| − | * | + | :* Si utilizamos la referencia recibida para modificar el estado de un objeto mutable, el cambio será visible fuera de la función, ya que ambas referencias permiten acceder al mismo objeto. |
| − | |||
| − | :<syntaxhighlight lang=" | + | :<syntaxhighlight lang="dart" enclose="div"> |
class Pelota { | class Pelota { | ||
double? peso; | double? peso; | ||
| Línea 39: | Línea 50: | ||
} | } | ||
| + | void modificarValores( | ||
| + | Pelota pelota, | ||
| + | int valor, | ||
| + | String cadena, | ||
| + | ) { | ||
| + | valor = 10; | ||
| + | cadena = 'NUEVA CADENA'; | ||
| − | |||
| − | |||
| − | |||
pelota.peso = 100.2; | pelota.peso = 100.2; | ||
pelota.id = 'P2'; | pelota.id = 'P2'; | ||
| − | |||
| − | |||
} | } | ||
| + | |||
void main() { | void main() { | ||
| − | |||
var valorPrueba = 5; | var valorPrueba = 5; | ||
var cadena = 'VIEJA CADENA'; | var cadena = 'VIEJA CADENA'; | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| + | final pelota = Pelota(); | ||
| + | |||
| + | pelota.id = 'P1'; | ||
| + | pelota.peso = 50.34; | ||
| + | |||
| + | modificarValores( | ||
| + | pelota, | ||
| + | valorPrueba, | ||
| + | cadena, | ||
| + | ); | ||
| + | |||
| + | print('Valor numérico: $valorPrueba'); | ||
| + | print('Cadena: $cadena'); | ||
| + | print('Objeto: ${pelota.id} -- ${pelota.peso}'); | ||
} | } | ||
| − | </syntaxhighlight> | + | </syntaxhighlight> |
| + | |||
| + | |||
| + | : Después de ejecutar la función: | ||
| + | :* <code>valorPrueba</code> sigue valiendo <code>5</code>. | ||
| + | :* <code>cadena</code> sigue conteniendo <code>'VIEJA CADENA'</code>. | ||
| + | :* Las propiedades del objeto <code>pelota</code> sí han cambiado. | ||
| + | : Por tanto, no es correcto decir que Dart pasa unos tipos por valor y otros por referencia. '''Todos los argumentos se pasan por valor'''; cuando trabajamos con objetos, el valor copiado es una referencia al objeto. | ||
<br /> | <br /> | ||
| − | ===Parámetros | + | ===Parámetros opcionales posicionales=== |
| − | * Podemos definir | + | <center> |
| − | :<syntaxhighlight lang=" | + | {{#ev:youtube|9a2ANaIjOa0}} |
| − | void imprimirMensaje([String mensaje='Nada enviado']){ | + | </center> |
| − | + | ||
| − | + | ||
| + | |||
| + | * Podemos definir parámetros posicionales opcionales escribiéndolos entre corchetes <code>[ ]</code>. | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div" highlight="1"> | ||
| + | void imprimirMensaje([String mensaje = 'Nada enviado']) { | ||
| + | print(mensaje); | ||
} | } | ||
void main() { | void main() { | ||
| − | + | imprimirMensaje(); | |
| − | + | imprimirMensaje('Nuevo mensaje'); | |
| − | + | } </syntaxhighlight> | |
| − | + | ||
| − | } | + | : En la primera llamada no enviamos ningún valor, por lo que el parámetro utiliza su valor por defecto. |
| − | </syntaxhighlight> | ||
| − | * | + | * Si el parámetro admite valores nulos, no es necesario establecer un valor por defecto: |
| − | : | ||
| − | |||
| + | :<syntaxhighlight lang="dart" enclose="div" highlight="1"> | ||
| + | void imprimirMensaje([String? mensaje]) { | ||
| + | print(mensaje); | ||
} | } | ||
| − | |||
| + | void main() { | ||
| + | imprimirMensaje(); | ||
| + | imprimirMensaje('Nuevo mensaje'); | ||
| + | } </syntaxhighlight> | ||
| − | + | : Si no enviamos ningún valor, <code>mensaje</code> tendrá el valor <code>null</code>. | |
| − | |||
| − | |||
| − | } | + | * Si una función tiene parámetros posicionales obligatorios y opcionales, los opcionales deben aparecer al final: |
| − | </syntaxhighlight> | + | |
| + | :<syntaxhighlight lang="dart" enclose="div" highlight="1"> | ||
| + | void imprimirMensaje(int numero, [String? mensaje]) { | ||
| + | print('$numero - $mensaje'); | ||
| + | } </syntaxhighlight> | ||
| + | <br /> | ||
| + | ===Parámetros con nombre=== | ||
| − | + | * Dart permite definir '''parámetros con nombre'''. | |
| − | + | : Para ello se escriben los parámetros entre llaves <code>{ }</code>. | |
| − | |||
| − | : | ||
| − | : | + | :<syntaxhighlight lang="dart" enclose="div"> |
| + | void imprimirMensaje({ | ||
| + | String? mensaje, | ||
| + | }) { | ||
| + | print(mensaje); | ||
| + | } </syntaxhighlight> | ||
| + | * Al realizar la llamada indicamos el nombre del parámetro: | ||
| − | + | :<syntaxhighlight lang="dart" enclose="div"> | |
| − | + | void main() { | |
| − | + | imprimirMensaje( | |
| − | </ | + | mensaje: 'Hola', |
| + | ); | ||
| + | } </syntaxhighlight> | ||
| + | * Una ventaja de los parámetros con nombre es que podemos indicar los argumentos por su nombre, haciendo las llamadas más fáciles de leer. | ||
<br /> | <br /> | ||
| − | |||
| − | * | + | ====Parámetros con nombre opcionales==== |
| − | + | ||
| − | :<syntaxhighlight lang=" | + | * Los parámetros con nombre son opcionales por defecto. |
| − | + | ||
| − | + | : Si el parámetro puede valer <code>null</code>, podemos declararlo como ''nullable'': | |
| − | + | ||
| − | void imprimirMensaje1({String? mensaje}){ | + | :<syntaxhighlight lang="dart" enclose="div"> |
| − | + | void imprimirMensaje1({ | |
| − | + | String? mensaje, | |
| + | }) { | ||
| + | print(mensaje); | ||
} | } | ||
| − | / | + | void main() { |
| − | + | imprimirMensaje1(); | |
| − | + | imprimirMensaje1( | |
| − | void imprimirMensaje2({String mensaje='Valor por defecto'}){ | + | mensaje: 'Hola', |
| − | + | ); | |
| − | + | } </syntaxhighlight> | |
| + | |||
| + | * Otra posibilidad es proporcionar un valor por defecto: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | void imprimirMensaje2({ | ||
| + | String mensaje = 'Valor por defecto', | ||
| + | }) { | ||
| + | print(mensaje); | ||
} | } | ||
| + | void main() { | ||
| + | imprimirMensaje2(); | ||
| + | |||
| + | imprimirMensaje2( | ||
| + | mensaje: 'Nuevo mensaje', | ||
| + | ); | ||
| + | } </syntaxhighlight> | ||
| + | |||
| + | <br /> | ||
| + | |||
| + | ====Parámetros con nombre obligatorios==== | ||
| + | |||
| + | * Podemos indicar que un parámetro con nombre es obligatorio utilizando la palabra clave <code>required</code>. | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div" highlight="2"> | ||
| + | void imprimirMensaje({ | ||
| + | required String mensaje, | ||
| + | }) { | ||
| + | print(mensaje); | ||
| + | } | ||
void main() { | void main() { | ||
| − | + | imprimirMensaje( | |
| − | + | mensaje: 'Mensaje', | |
| − | + | ); | |
| − | + | } </syntaxhighlight> | |
| − | + | ||
| + | : En este caso estamos obligados a enviar el argumento correspondiente a <code>mensaje</code>. | ||
| + | |||
| + | : Como el parámetro es obligatorio y su tipo es <code>String</code>, no necesitamos asignarle un valor por defecto ni permitir que sea <code>null</code>. | ||
| − | </ | + | <br /> |
| + | ====Combinación de parámetros posicionales y con nombre==== | ||
| − | * | + | * Podemos combinar parámetros posicionales y parámetros con nombre. |
| − | |||
| − | |||
| − | : | + | : Los parámetros posicionales se escriben antes que los parámetros con nombre: |
| − | :<syntaxhighlight lang=" | + | :<syntaxhighlight lang="dart" enclose="div" highlight="2,3"> |
| − | void imprimirMensaje({ | + | void imprimirMensaje( |
| − | + | int numero, { | |
| − | print(mensaje); | + | String? mensaje, |
| + | } | ||
| + | ) { | ||
| + | print( | ||
| + | 'El número es $numero y el mensaje es $mensaje', | ||
| + | ); | ||
} | } | ||
void main() { | void main() { | ||
| − | + | imprimirMensaje( | |
| − | imprimirMensaje(mensaje: 'Mensaje'); | + | 10, |
| − | + | mensaje: 'Mensaje', | |
| + | ); | ||
} | } | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | <br /> | ||
| + | |||
| + | ==Funciones flecha o ''arrow functions''== | ||
| + | |||
| + | * Cuando una función está formada por una única expresión, podemos utilizar la sintaxis de '''función flecha''' mediante el operador <code>=></code>. | ||
| + | |||
| + | : Por ejemplo, la siguiente función: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | double sumar(double a, double b) { | ||
| + | return a + b; | ||
| + | } </syntaxhighlight> | ||
| + | |||
| + | : puede escribirse como: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | double sumar(double a, double b) => a + b; </syntaxhighlight> | ||
| + | |||
| + | * Después de <code>=></code> se escribe una expresión. | ||
| + | |||
| + | : El resultado de dicha expresión será el valor devuelto por la función cuando esta tenga un tipo de retorno distinto de <code>void</code>. | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | double calcularMedia( | ||
| + | double num1, | ||
| + | double num2, | ||
| + | ) => | ||
| + | (num1 + num2) / 2; </syntaxhighlight> | ||
| − | </ | + | * También podemos emplearla con una expresión que no produzca un valor útil, como una llamada a <code>print</code>: |
| − | + | :<syntaxhighlight lang="dart" enclose="div"> | |
| − | :<syntaxhighlight lang=" | + | void imprimirMediaNumero({ |
| − | void | + | required int num1, |
| − | + | required int num2, | |
| − | + | }) => | |
| − | + | print((num1 + num2) / 2); | |
void main() { | void main() { | ||
| − | + | imprimirMediaNumero( | |
| − | + | num1: 10, | |
| − | + | num2: 15, | |
| − | } | + | ); |
| + | } </syntaxhighlight> | ||
| + | |||
| + | * Si necesitamos ejecutar varias instrucciones, debemos emplear un bloque de código con llaves: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | void mostrarDatos(int numero) { | ||
| + | print('Número recibido:'); | ||
| + | print(numero); | ||
| + | } </syntaxhighlight> | ||
| + | |||
| + | <br /> | ||
| + | |||
| + | ==Funciones anónimas== | ||
| + | |||
| + | * Una '''función anónima''' es una función que no tiene un nombre asociado. | ||
| + | |||
| + | : Las funciones anónimas son especialmente útiles como argumentos de métodos que esperan recibir otra función. | ||
| + | |||
| + | : Este tipo de métodos aparece frecuentemente cuando trabajamos con colecciones y también en Flutter mediante ''callbacks''. | ||
| + | |||
| + | * Una función anónima puede tener un bloque de código: | ||
| − | </syntaxhighlight> | + | :<syntaxhighlight lang="dart" enclose="div"> |
| + | (elemento) { | ||
| + | print(elemento); | ||
| + | } </syntaxhighlight> | ||
| + | * Si únicamente necesitamos una expresión, podemos emplear la sintaxis flecha: | ||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | (elemento) => print(elemento) </syntaxhighlight> | ||
<br /> | <br /> | ||
| − | ==Funciones | + | ===Funciones anónimas y forEach=== |
| + | |||
| + | * El método <code>forEach</code> de un <code>Iterable</code> espera recibir una función que será ejecutada una vez por cada elemento. | ||
| − | + | : Por ejemplo: | |
| − | : | ||
| − | : | ||
| − | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | void main() { | ||
| + | final lista = <int>[1, 2, 3, 4, 5, 6]; | ||
| − | + | lista.forEach((elemento) { | |
| − | + | print('Accediendo al elemento $elemento'); | |
| − | + | }); | |
| − | </syntaxhighlight> | + | } </syntaxhighlight> |
| + | : El parámetro <code>elemento</code> representa, en cada ejecución, uno de los elementos de la lista. | ||
| + | * Podemos expresar lo mismo mediante una función flecha: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | void main() { | ||
| + | final lista = <int>[1, 2, 3, 4, 5, 6]; | ||
| + | |||
| + | lista.forEach( | ||
| + | (elemento) => print( | ||
| + | 'Accediendo al elemento $elemento', | ||
| + | ), | ||
| + | ); | ||
| + | } </syntaxhighlight> | ||
| + | |||
| + | <u>'''Nota importante:'''</u> | ||
| + | |||
| + | : Aunque estas formas son válidas y sirven para comprender el funcionamiento de las funciones anónimas, para realizar un recorrido sencillo de un <code>Iterable</code>, Dart recomienda normalmente utilizar <code>for...in</code> en lugar de <code>forEach</code> con una función anónima. | ||
: Por ejemplo: | : Por ejemplo: | ||
| − | |||
| − | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
void main() { | void main() { | ||
| + | final lista = <int>[1, 2, 3, 4, 5, 6]; | ||
| − | + | for (final elemento in lista) { | |
| + | print( | ||
| + | 'Accediendo al elemento $elemento', | ||
| + | ); | ||
} | } | ||
| − | </syntaxhighlight> | + | } </syntaxhighlight> |
| + | <br /> | ||
| + | ===Pasar una función existente como parámetro=== | ||
| − | * | + | * Cuando un método espera una función como parámetro, no siempre tenemos que crear una función anónima. |
| − | |||
| − | |||
| + | : Podemos pasar una función que ya esté definida: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | void recorrerConjunto(String elemento) { | ||
| + | print(elemento); | ||
| + | } | ||
void main() { | void main() { | ||
| + | final conjunto = <String>{ | ||
| + | 'uno', | ||
| + | 'dos', | ||
| + | 'tres', | ||
| + | }; | ||
| + | |||
| + | conjunto.forEach(recorrerConjunto); | ||
| + | } </syntaxhighlight> | ||
| + | |||
| + | : Fijarse en que escribimos: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | conjunto.forEach(recorrerConjunto); </syntaxhighlight> | ||
| + | |||
| + | : y no: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | conjunto.forEach(recorrerConjunto()); </syntaxhighlight> | ||
| + | |||
| + | : No queremos ejecutar <code>recorrerConjunto</code> en ese momento. Queremos proporcionar la función a <code>forEach</code> para que sea este método quien la ejecute posteriormente una vez por cada elemento. | ||
| − | + | * La función que enviemos debe ser compatible con el tipo de función que espera el método. | |
| − | |||
| − | |||
| + | : En este ejemplo, el conjunto contiene elementos de tipo <code>String</code>, por lo que la función recibe un <code>String</code> y devuelve <code>void</code>: | ||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | void recorrerConjunto(String elemento) { | ||
| + | print(elemento); | ||
| + | } </syntaxhighlight> | ||
<br /> | <br /> | ||
| − | == | + | ===Tear-offs=== |
| − | * | + | |
| − | : | + | * Cuando hacemos referencia a una función o método sin utilizar los paréntesis de llamada, Dart crea lo que se denomina un '''tear-off'''. |
| + | |||
| + | : Por ejemplo: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | conjunto.forEach(recorrerConjunto); </syntaxhighlight> | ||
| + | |||
| + | : <code>recorrerConjunto</code> representa la función que queremos proporcionar a <code>forEach</code>. | ||
| + | |||
| + | * Dart recomienda no crear una función anónima innecesaria cuando podemos pasar directamente una función existente. | ||
| + | |||
| + | : Por tanto, en lugar de: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | lista.forEach( | ||
| + | (elemento) => gestionarElemento(elemento), | ||
| + | ); </syntaxhighlight> | ||
| + | |||
| + | : es preferible: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | lista.forEach(gestionarElemento); </syntaxhighlight> | ||
| + | |||
| + | * Incluso podemos proporcionar directamente una función existente de Dart si es compatible con los parámetros esperados: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | lista.forEach(print); </syntaxhighlight> | ||
| − | + | <br /> | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | ===Diferentes formas de proporcionar una función a forEach=== | |
| − | |||
| − | |||
| − | |||
| − | + | * Veamos las diferentes posibilidades: | |
| − | + | :<syntaxhighlight lang="dart" enclose="div"> | |
| − | + | void gestionarElemento(int elemento) { | |
| − | + | print('Elemento: $elemento'); | |
} | } | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
void main() { | void main() { | ||
| + | final lista = <int>[1, 2, 3, 4, 5, 6]; | ||
| + | |||
| + | // Función anónima con bloque. | ||
| + | lista.forEach((elemento) { | ||
| + | print('Función anónima: $elemento'); | ||
| + | }); | ||
| + | |||
| + | // Función anónima flecha. | ||
| + | lista.forEach( | ||
| + | (elemento) => | ||
| + | print('Función flecha: $elemento'), | ||
| + | ); | ||
| + | |||
| + | // Función previamente definida. | ||
| + | lista.forEach(gestionarElemento); | ||
| + | |||
| + | // Función existente compatible. | ||
| + | lista.forEach(print); | ||
| + | } </syntaxhighlight> | ||
| + | |||
| + | : Las dos primeras formas son útiles para comprender cómo funcionan las funciones anónimas. | ||
| + | |||
| + | : Sin embargo, si únicamente queremos recorrer un <code>Iterable</code>, normalmente emplearemos: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | for (final elemento in lista) { | ||
| + | print(elemento); | ||
| + | } </syntaxhighlight> | ||
| + | |||
| + | : Si queremos ejecutar exactamente una función existente para cada elemento, <code>forEach</code> es apropiado: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | lista.forEach(gestionarElemento); | ||
| + | lista.forEach(print); </syntaxhighlight> | ||
| + | |||
| + | <br /> | ||
| + | |||
| + | ===Funciones que devuelven funciones=== | ||
| + | |||
| + | * En Dart, las funciones son objetos. Esto significa que una función también puede devolver otra función. | ||
| − | + | : Veamos un ejemplo: | |
| − | + | :<syntaxhighlight lang="dart" enclose="div"> | |
| − | + | void Function(int) crearFuncion() { | |
| − | + | return (int elemento) { | |
| + | print('Número: $elemento'); | ||
| + | }; | ||
} | } | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
void main() { | void main() { | ||
| + | final lista = <int>[1, 2, 3]; | ||
| + | |||
| + | lista.forEach(crearFuncion()); | ||
| + | } </syntaxhighlight> | ||
| + | |||
| + | : En este caso ocurre algo diferente al ejemplo anterior. | ||
| + | |||
| + | : Primero se ejecuta: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | crearFuncion() </syntaxhighlight> | ||
| + | |||
| + | : Esta función devuelve otra función compatible con la función que espera <code>forEach</code>. | ||
| + | |||
| + | : Por ese motivo, en este caso sí aparecen los paréntesis después de <code>crearFuncion</code>. | ||
| + | |||
| + | : Este tipo de funciones recibe el nombre de '''funciones de orden superior'''. También están relacionadas con el concepto de ''closures'', que veremos cuando sea necesario. | ||
| + | |||
| + | <br /> | ||
| − | + | ===forEach en mapas=== | |
| − | + | * Los objetos de tipo <code>Map</code> también disponen del método <code>forEach</code>. | |
| − | + | ||
| − | < | + | : En este caso, la función recibe dos parámetros: |
| − | + | ||
| − | + | :* La clave. | |
| + | :* El valor asociado a dicha clave. | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | void main() { | ||
| + | final mapa = <String, double>{ | ||
| + | 'base': 1.84, | ||
| + | 'alero': 1.95, | ||
| + | 'pivot': 2.10, | ||
| + | 'escolta': 1.90, | ||
| + | }; | ||
| + | |||
| + | mapa.forEach((clave, valor) { | ||
| + | print( | ||
| + | 'La clave $clave tiene el valor $valor', | ||
| + | ); | ||
| + | }); | ||
| + | } </syntaxhighlight> | ||
| + | : En el caso de <code>Map.forEach</code>, el empleo de una función anónima es totalmente apropiado. | ||
| + | * Si queremos recorrer solamente las claves o solamente los valores, podemos emplear <code>for...in</code>: | ||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | void main() { | ||
| + | final mapa = <String, double>{ | ||
| + | 'base': 1.84, | ||
| + | 'alero': 1.95, | ||
| + | 'pivot': 2.10, | ||
| + | 'escolta': 1.90, | ||
| + | }; | ||
| − | + | for (final clave in mapa.keys) { | |
| − | + | print('Clave: $clave'); | |
| − | |||
| − | |||
| − | |||
} | } | ||
| − | + | for (final valor in mapa.values) { | |
| − | + | print('Valor: $valor'); | |
| − | |||
| − | |||
} | } | ||
| + | } </syntaxhighlight> | ||
| − | void | + | <br /> |
| − | + | ||
| + | ===Recorrido de conjuntos=== | ||
| + | |||
| + | * Los conjuntos (<code>Set</code>) son objetos <code>Iterable</code>, por lo que para un recorrido sencillo podemos utilizar <code>for...in</code>: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | void main() { | ||
| + | final conjunto = <String>{ | ||
| + | 'Ángel', | ||
| + | 'Luis', | ||
| + | 'Ana', | ||
| + | 'María', | ||
| + | }; | ||
| + | |||
| + | for (final elemento in conjunto) { | ||
| + | print(elemento); | ||
} | } | ||
| + | } </syntaxhighlight> | ||
| + | * También podemos utilizar <code>forEach</code> si proporcionamos directamente una función existente: | ||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
void main() { | void main() { | ||
| + | final conjunto = <String>{ | ||
| + | 'Ángel', | ||
| + | 'Luis', | ||
| + | 'Ana', | ||
| + | 'María', | ||
| + | }; | ||
| − | + | conjunto.forEach(print); | |
| + | } </syntaxhighlight> | ||
| − | + | <br /> | |
| − | |||
| − | + | ==Método where== | |
| − | + | * El método <code>where</code> permite '''filtrar''' los elementos de un <code>Iterable</code>. | |
| − | + | : Recibe como parámetro una función que debe devolver un valor booleano: | |
| − | } | + | |
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | (elemento) => condición </syntaxhighlight> | ||
| + | |||
| + | : Si la función devuelve <code>true</code>, el elemento formará parte del resultado. | ||
| + | |||
| + | : Si devuelve <code>false</code>, el elemento será descartado. | ||
| + | |||
| + | * Por ejemplo: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | void main() { | ||
| + | final numeros = <int>[ | ||
| + | 1, | ||
| + | 2, | ||
| + | 3, | ||
| + | 4, | ||
| + | 5, | ||
| + | 6, | ||
| + | ]; | ||
| + | |||
| + | final pares = numeros.where( | ||
| + | (numero) => numero.isEven, | ||
| + | ); | ||
| + | |||
| + | print(pares); | ||
| + | } </syntaxhighlight> | ||
| + | |||
| + | : <code>where</code> devuelve un <code>Iterable</code>. | ||
| + | |||
| + | * Si queremos obtener una lista: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | final pares = numeros | ||
| + | .where((numero) => numero.isEven) | ||
| + | .toList(); </syntaxhighlight> | ||
| − | + | * Si queremos obtener un conjunto: | |
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | final pares = numeros | ||
| + | .where((numero) => numero.isEven) | ||
| + | .toSet(); </syntaxhighlight> | ||
<br /> | <br /> | ||
| − | + | ==Método every== | |
| − | * ''' | + | * El método <code>every</code> permite comprobar si '''todos''' los elementos de un <code>Iterable</code> cumplen una determinada condición. |
| − | :<syntaxhighlight lang=" | + | |
| + | : Devuelve un valor de tipo <code>bool</code>. | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
void main() { | void main() { | ||
| + | final numeros = <int>[ | ||
| + | 10, | ||
| + | 20, | ||
| + | 30, | ||
| + | 40, | ||
| + | ]; | ||
| − | + | final todosPositivos = numeros.every( | |
| − | + | (numero) => numero > 0, | |
| − | + | ); | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| + | print(todosPositivos); | ||
| + | } </syntaxhighlight> | ||
| + | : El resultado será <code>true</code> únicamente si todos los elementos cumplen la condición. | ||
<br /> | <br /> | ||
| − | * | + | |
| − | :<syntaxhighlight lang=" | + | ==Método map aplicado a List y Set== |
| + | |||
| + | * El método <code>map</code> permite transformar los elementos de un <code>Iterable</code>. | ||
| + | |||
| + | : Por cada elemento de la colección original se ejecuta una función que genera un nuevo valor. | ||
| + | |||
| + | : Como resultado obtenemos un nuevo <code>Iterable</code>. | ||
| + | |||
| + | * Por ejemplo: | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
void main() { | void main() { | ||
| + | final numeros = <int>[ | ||
| + | 1, | ||
| + | 2, | ||
| + | 3, | ||
| + | 4, | ||
| + | ]; | ||
| + | |||
| + | final cuadrados = numeros.map( | ||
| + | (numero) => numero * numero, | ||
| + | ); | ||
| − | + | print(cuadrados); | |
| − | + | } </syntaxhighlight> | |
| − | |||
| − | + | * Habitualmente convertiremos el resultado a una lista mediante <code>toList()</code>: | |
| − | </syntaxhighlight> | + | |
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | final cuadrados = numeros | ||
| + | .map((numero) => numero * numero) | ||
| + | .toList(); </syntaxhighlight> | ||
| + | |||
| + | * Este método es especialmente útil en Flutter, ya que permite crear una colección de ''widgets'' a partir de una colección de datos. | ||
| + | : Por ejemplo, conceptualmente podemos transformar una lista de cadenas en una lista de objetos: | ||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | void main() { | ||
| + | final nombres = <String>[ | ||
| + | 'Ana', | ||
| + | 'Luis', | ||
| + | 'María', | ||
| + | ]; | ||
| − | + | final mensajes = nombres | |
| + | .map((nombre) => 'Hola $nombre') | ||
| + | .toList(); | ||
| − | + | print(mensajes); | |
| − | + | } </syntaxhighlight> | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | } | ||
| − | </syntaxhighlight> | ||
| + | * También podemos aplicar <code>map</code> sobre las entradas de un <code>Map</code>: | ||
| − | : | + | :<syntaxhighlight lang="dart" enclose="div"> |
| + | void main() { | ||
| + | final mapa = <String, String>{ | ||
| + | 'nombre': 'Ángel', | ||
| + | 'apellido1': 'Fernández', | ||
| + | 'apellido2': 'González', | ||
| + | }; | ||
| + | final lista = mapa.entries | ||
| + | .map( | ||
| + | (entrada) => 'HOLA: ${entrada.value}', | ||
| + | ) | ||
| + | .toList(); | ||
| + | print(lista); | ||
| + | } </syntaxhighlight> | ||
<br /> | <br /> | ||
| − | == | + | ==List.generate== |
| + | |||
| + | * La clase <code>List</code> dispone del constructor <code>List.generate</code>, que permite generar los elementos de una lista mediante una función. | ||
| + | |||
| + | : La función recibe la posición que se está generando: | ||
| − | + | :<syntaxhighlight lang="dart" enclose="div"> | |
| + | void main() { | ||
| + | final numeros = List<int>.generate( | ||
| + | 5, | ||
| + | (posicion) => posicion, | ||
| + | ); | ||
| + | print(numeros); | ||
| + | } </syntaxhighlight> | ||
| − | + | : El resultado será: | |
| − | : | ||
| − | : | ||
| − | + | :<syntaxhighlight lang="text" enclose="div"> | |
| − | :<syntaxhighlight lang=" | + | [0, 1, 2, 3, 4] </syntaxhighlight> |
| − | |||
| − | + | * Si queremos generar los números desde 1: | |
| − | |||
| − | |||
| − | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | final numeros = List<int>.generate( | ||
| + | 5, | ||
| + | (posicion) => posicion + 1, | ||
| + | ); </syntaxhighlight> | ||
| + | * Podemos indicar mediante <code>growable: false</code> que la lista tenga longitud fija: | ||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | final numeros = List<int>.generate( | ||
| + | 5, | ||
| + | (posicion) => posicion + 1, | ||
| + | growable: false, | ||
| + | ); </syntaxhighlight> | ||
| + | <u>'''Importante:'''</u> | ||
| + | : Una lista con <code>growable: false</code> es una '''lista de longitud fija'''. Esto significa que no podemos añadir ni eliminar elementos. | ||
| + | : Sin embargo, esto no significa que la lista sea inmutable. Podemos modificar el valor almacenado en una posición existente. | ||
<br /> | <br /> | ||
| Línea 407: | Línea 817: | ||
'''Ejercicio 1:''' | '''Ejercicio 1:''' | ||
| − | Crea una función que espere recibir | + | Crea una función que espere recibir mediante parámetros con nombre una posición y una lista de cadenas, y devuelva el valor de la lista que se encuentra en la posición indicada. |
| − | : Si no se envía posición debe | + | |
| − | : Si la lista | + | :* La lista debe ser enviada obligatoriamente. |
| − | : Si la posición | + | :* Si no se envía la posición, debe utilizarse la posición <code>0</code>. |
| + | :* Si la lista no tiene elementos, debe devolver la cadena <code>'Sin datos'</code>. | ||
| + | :* Si la posición es negativa o no existe en la lista, debe devolver la cadena <code>'Índice fuera de rango'</code>. | ||
| + | :* Llama a la función empleando diferentes valores para comprobar su funcionamiento. | ||
| + | <br /> | ||
'''Ejercicio 2:''' | '''Ejercicio 2:''' | ||
| − | |||
| − | + | Crea un mapa de datos en el que se guarde como clave un índice entero y como valor un precio. | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| + | :* Define inicialmente cuatro entradas en el mapa. | ||
| + | :* Añade dos nuevas entradas. | ||
| + | :* Borra la última entrada introducida. | ||
| + | :* Recorre el mapa empleando <code>forEach</code>. La función debe mostrar la clave y el precio de todos los platos cuyo precio sea inferior a 5 euros. | ||
| + | :* Crea una función de nombre <code>getNumPlatosMasCaros</code> a la que se le pase mediante parámetros con nombre un mapa y un precio. | ||
| + | :* Los dos parámetros deben ser obligatorios y no admitir valores nulos. | ||
| + | :* La función debe devolver cuántos platos tienen un precio superior al indicado. | ||
| + | :* Emplea <code>where</code> para realizar el filtrado. | ||
| + | :* Llama a la función para comprobar que funciona. | ||
| + | <br /> | ||
'''Ejercicio 3:''' | '''Ejercicio 3:''' | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | Crea un conjunto de nombre <code>equipo</code> que contenga los valores: | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | : | + | :<syntaxhighlight lang="text" enclose="div"> |
| + | Base | ||
| + | Alero | ||
| + | Pivot | ||
| + | Escolta </syntaxhighlight> | ||
| − | + | Crea una función que reciba como parámetros el conjunto y una cadena a buscar. | |
| − | Crea | ||
| − | |||
| − | |||
| + | :* Emplea el método <code>where</code>. | ||
| + | :* La función debe mostrar los elementos del conjunto que contengan la cadena indicada. | ||
| + | :* Recorre el resultado empleando <code>for...in</code>. | ||
| + | :* Realiza una búsqueda con la cadena <code>'as'</code>. | ||
| + | :* Realiza otra búsqueda con la cadena <code>'s'</code>. | ||
| + | <br /> | ||
'''Ejercicio 4:''' | '''Ejercicio 4:''' | ||
| − | + | ||
| − | + | Define una lista de números decimales con los siguientes valores: | |
| − | Crea una función de nombre | + | |
| − | + | :<syntaxhighlight lang="text" enclose="div"> | |
| − | Llama a | + | 10.0, 11.22, 15.0, 7.0, 3.44 </syntaxhighlight> |
| − | + | ||
| − | + | Crea una función de nombre <code>sonMayores</code>: | |
| − | + | ||
| + | :* Debe recibir mediante parámetros con nombre un número y una lista de números decimales. | ||
| + | :* La lista debe ser obligatoria. | ||
| + | :* Si no se envía el número, debe utilizarse el valor <code>0</code>. | ||
| + | :* Emplea el método <code>every</code> para comprobar si todos los elementos de la lista son mayores que el número indicado. | ||
| + | :* La función debe devolver la cadena <code>'SI'</code> o <code>'NO'</code>. | ||
| + | :* Llama a la función empleando los valores <code>2</code> y <code>12</code>. | ||
| + | :* Muestra una frase con el formato: | ||
| + | |||
| + | :<syntaxhighlight lang="text" enclose="div"> | ||
| + | Todos los números de la lista son mayores a XX: YY </syntaxhighlight> | ||
| + | |||
| + | : siendo <code>XX</code> el número utilizado e <code>YY</code> el resultado <code>SI</code> o <code>NO</code>. | ||
| + | |||
| + | <br /> | ||
'''Ejercicio 5:''' | '''Ejercicio 5:''' | ||
| − | |||
| − | |||
| − | + | Emplea <code>List.generate</code> para realizar los siguientes ejercicios: | |
| − | |||
| + | :* Genera una lista de 100 números enteros aleatorios comprendidos entre 1 y 100. Emplea la clase Random. | ||
| + | :* Genera una lista con los números comprendidos entre 1 y 100 y haz que tenga longitud fija, de forma que no puedan añadirse ni eliminarse elementos. | ||
| + | :* Dado un número, genera una lista con los cuadrados de todos los números comprendidos entre 1 y dicho número. En este caso, la lista debe permitir añadir nuevos elementos. | ||
| + | <br /> | ||
| − | ''' | + | '''Ejercicio adicional:''' |
| − | |||
| + | Investiga otros métodos disponibles en <code>List</code>, <code>Set</code>, <code>Map</code> o <code>Iterable</code> que reciban una función como parámetro. | ||
| + | :* Busca varios ejemplos. | ||
| + | :* Indica qué tipo de función espera cada método. | ||
| + | :* Realiza un pequeño ejemplo de uso. | ||
| + | :* Algunos métodos que puedes investigar son <code>any</code>, <code>firstWhere</code>, <code>fold</code>, <code>removeWhere</code> o <code>updateAll</code>. | ||
| + | <br /> | ||
| − | + | ===Soluciones de los ejercicios propuestos=== | |
| − | ===Soluciones | ||
'''Solución Ejercicio 1:''' | '''Solución Ejercicio 1:''' | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | :<syntaxhighlight lang=" | + | :<syntaxhighlight lang="dart" enclose="div"> |
| − | String devolverElementoLista( | + | String devolverElementoLista({ |
| − | + | int posicion = 0, | |
| − | { | + | required List<String> lista, |
| − | + | }) { | |
| − | + | if (lista.isEmpty) { | |
| − | + | return 'Sin datos'; | |
| − | + | } | |
| + | |||
| + | if ( | ||
| + | posicion < 0 || | ||
| + | posicion >= lista.length | ||
| + | ) { | ||
| + | return 'Índice fuera de rango'; | ||
| + | } | ||
| + | |||
| + | return lista[posicion]; | ||
} | } | ||
void main() { | void main() { | ||
| + | final lista = <String>[ | ||
| + | 'Uno', | ||
| + | 'Dos', | ||
| + | 'Tres', | ||
| + | 'Cuatro', | ||
| + | ]; | ||
| + | |||
| + | print( | ||
| + | devolverElementoLista( | ||
| + | lista: lista, | ||
| + | ), | ||
| + | ); | ||
| + | |||
| + | print( | ||
| + | devolverElementoLista( | ||
| + | posicion: 2, | ||
| + | lista: lista, | ||
| + | ), | ||
| + | ); | ||
| − | + | print( | |
| − | + | devolverElementoLista( | |
| − | + | lista: lista, | |
| − | + | posicion: 4, | |
| − | + | ), | |
| − | + | ); | |
| + | print( | ||
| + | devolverElementoLista( | ||
| + | posicion: -1, | ||
| + | lista: lista, | ||
| + | ), | ||
| + | ); | ||
} | } | ||
| − | |||
</syntaxhighlight> | </syntaxhighlight> | ||
| + | <br /> | ||
'''Solución Ejercicio 2:''' | '''Solución Ejercicio 2:''' | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | int getNumPlatosMasCaros({required Map mapa, required double precio}){ | + | :<syntaxhighlight lang="dart" enclose="div"> |
| − | + | int getNumPlatosMasCaros({ | |
| − | + | required Map<int, double> mapa, | |
| − | + | required double precio, | |
| − | + | }) { | |
| − | + | return mapa.values | |
| − | + | .where( | |
| + | (precioMapa) => precioMapa > precio, | ||
| + | ) | ||
| + | .length; | ||
} | } | ||
void main() { | void main() { | ||
| + | final precios = <int, double>{ | ||
| + | 1: 10.34, | ||
| + | 2: 3.24, | ||
| + | 3: 24.99, | ||
| + | 4: 17.35, | ||
| + | }; | ||
| + | |||
| + | precios.addAll({ | ||
| + | 5: 9.23, | ||
| + | 6: 2.22, | ||
| + | }); | ||
| − | + | print(precios); | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | final ultimaClave = precios.keys.last; | |
| − | + | precios.remove(ultimaClave); | |
| − | + | ||
| − | + | precios.forEach((clave, valor) { | |
| − | + | if (valor < 5) { | |
| − | + | print( | |
| − | + | 'La clave $clave tiene un precio de $valor', | |
| − | + | ); | |
| − | + | } | |
| − | + | }); | |
| + | |||
| + | const precioReferencia = 10.0; | ||
| + | |||
| + | final numPlatos = getNumPlatosMasCaros( | ||
| + | mapa: precios, | ||
| + | precio: precioReferencia, | ||
| + | ); | ||
| + | |||
| + | print( | ||
| + | 'Número de platos con un precio superior ' | ||
| + | 'a $precioReferencia euros: $numPlatos', | ||
| + | ); | ||
} | } | ||
| − | |||
</syntaxhighlight> | </syntaxhighlight> | ||
| − | + | <br /> | |
'''Solución Ejercicio 3:''' | '''Solución Ejercicio 3:''' | ||
| − | |||
| − | |||
| − | |||
| − | :<syntaxhighlight lang=" | + | :<syntaxhighlight lang="dart" enclose="div"> |
| − | void | + | void buscarCadena( |
| + | Set<String> conjunto, | ||
| + | String cadena, | ||
| + | ) { | ||
| + | final encontrados = conjunto.where( | ||
| + | (elemento) => elemento.contains(cadena), | ||
| + | ); | ||
| − | + | for (final elemento in encontrados) { | |
| − | + | print('Encontrado: $elemento'); | |
| + | } | ||
| + | } | ||
| − | |||
void main() { | void main() { | ||
| + | final equipo = <String>{ | ||
| + | 'Base', | ||
| + | 'Alero', | ||
| + | 'Pivot', | ||
| + | 'Escolta', | ||
| + | }; | ||
| + | |||
| + | var cadenaBusqueda = 'as'; | ||
| + | |||
| + | print( | ||
| + | 'Cadena de búsqueda: $cadenaBusqueda', | ||
| + | ); | ||
| − | + | buscarCadena( | |
| − | + | equipo, | |
| − | + | cadenaBusqueda, | |
| − | + | ); | |
| − | |||
| − | + | cadenaBusqueda = 's'; | |
| − | + | ||
| − | + | print( | |
| − | + | 'Cadena de búsqueda: $cadenaBusqueda', | |
| + | ); | ||
| + | |||
| + | buscarCadena( | ||
| + | equipo, | ||
| + | cadenaBusqueda, | ||
| + | ); | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| − | + | <br /> | |
'''Solución Ejercicio 4:''' | '''Solución Ejercicio 4:''' | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | :<syntaxhighlight lang=" | + | :<syntaxhighlight lang="dart" enclose="div"> |
| − | String sonMayores({numero = 0, required List lista}) => lista.every(( | + | String sonMayores({ |
| + | double numero = 0, | ||
| + | required List<double> lista, | ||
| + | }) => | ||
| + | lista.every( | ||
| + | (elemento) => elemento > numero, | ||
| + | ) | ||
| + | ? 'SI' | ||
| + | : 'NO'; | ||
| + | void main() { | ||
| + | final listaNumeros = <double>[ | ||
| + | 10.0, | ||
| + | 11.22, | ||
| + | 15.0, | ||
| + | 7.0, | ||
| + | 3.44, | ||
| + | ]; | ||
| + | var numeroReferencia = 2.0; | ||
| − | + | print( | |
| + | 'Todos los números de la lista son mayores ' | ||
| + | 'a $numeroReferencia: ' | ||
| + | '${sonMayores( | ||
| + | numero: numeroReferencia, | ||
| + | lista: listaNumeros, | ||
| + | )}', | ||
| + | ); | ||
| − | + | numeroReferencia = 12.0; | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | + | print( | |
| − | + | 'Todos los números de la lista son mayores ' | |
| − | + | 'a $numeroReferencia: ' | |
| + | '${sonMayores( | ||
| + | lista: listaNumeros, | ||
| + | numero: numeroReferencia, | ||
| + | )}', | ||
| + | ); | ||
} | } | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | <br /> | ||
| + | |||
| + | '''Solución Ejercicio 5:''' | ||
| + | |||
| + | :<syntaxhighlight lang="dart" enclose="div"> | ||
| + | import 'dart:math'; | ||
| + | |||
| + | void main() { | ||
| + | final random = Random(); | ||
| + | |||
| + | // 100 números aleatorios entre 1 y 100. | ||
| + | final numerosAleatorios = List<int>.generate( | ||
| + | 100, | ||
| + | (_) => random.nextInt(100) + 1, | ||
| + | ); | ||
| − | + | print(numerosAleatorios); | |
| + | // Números desde 1 hasta 100. | ||
| + | // Lista de longitud fija. | ||
| + | final numerosSecuenciales = List<int>.generate( | ||
| + | 100, | ||
| + | (posicion) => posicion + 1, | ||
| + | growable: false, | ||
| + | ); | ||
| + | print(numerosSecuenciales); | ||
| + | // Cuadrados desde 1 hasta numero. | ||
| + | // La lista permite añadir elementos. | ||
| + | const numero = 10; | ||
| − | + | final numerosAlCuadrado = List<int>.generate( | |
| − | + | numero, | |
| − | + | (posicion) { | |
| + | final valor = posicion + 1; | ||
| − | + | return valor * valor; | |
| − | + | }, | |
| + | growable: true, | ||
| + | ); | ||
| − | + | print(numerosAlCuadrado); | |
| − | + | numerosAlCuadrado.add(121); | |
| − | + | print(numerosAlCuadrado); | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| − | |||
| Línea 648: | Línea 1182: | ||
[https://wiki.cifprodolfoucha.es/index.php?title=Programaci%C3%B3n_de_dispositivos_m%C3%B3viles_con_FLUTTER Enlace a la página principal del curso] | [https://wiki.cifprodolfoucha.es/index.php?title=Programaci%C3%B3n_de_dispositivos_m%C3%B3viles_con_FLUTTER Enlace a la página principal del curso] | ||
| − | + | <br /> | |
| − | + | ``` | |
| − | |||
| − | <br> | ||
Revisión actual del 07:34 24 sep 2026
Sumario
Introducción
- En Dart, las funciones permiten agrupar código que puede ejecutarse desde diferentes partes de un programa.
: Como este curso está orientado a alumnado que ya sabe programar en algún lenguaje, no profundizaremos en los conceptos generales de las funciones, sino en las características específicas que ofrece Dart.
- Es recomendable indicar el tipo de los parámetros que recibe una función y el tipo del dato que devuelve.
- Veamos un ejemplo de función con parámetros:
double sumarValores(double val1, double val2) { return val1 + val2; } void main() { final num1 = 10.32; final num2 = 1.35; print(sumarValores(num1, num2)); }
Paso de parámetros
- Al igual que en otros lenguajes, podemos pasar datos a las funciones empleando parámetros.
- Como ya vimos anteriormente, podemos definir parámetros que admitan valores nulos utilizando los tipos nullable, como por ejemplo
String?,int?odouble?.
Paso por valor
- En Dart, los argumentos de una función se pasan por valor.
- Las variables de Dart contienen referencias a objetos. Cuando enviamos una variable como argumento de una función, se copia el valor almacenado en dicha variable.
- Si dicho valor es una referencia a un objeto, se copia la referencia. Por tanto, tanto la variable original como el parámetro pueden hacer referencia al mismo objeto.
- Esto provoca dos comportamientos que debemos diferenciar:
- Si reasignamos el parámetro dentro de la función, no modificamos la variable utilizada en la llamada.
- Si utilizamos la referencia recibida para modificar el estado de un objeto mutable, el cambio será visible fuera de la función, ya que ambas referencias permiten acceder al mismo objeto.
class Pelota { double? peso; String? id; } void modificarValores( Pelota pelota, int valor, String cadena, ) { valor = 10; cadena = 'NUEVA CADENA'; pelota.peso = 100.2; pelota.id = 'P2'; } void main() { var valorPrueba = 5; var cadena = 'VIEJA CADENA'; final pelota = Pelota(); pelota.id = 'P1'; pelota.peso = 50.34; modificarValores( pelota, valorPrueba, cadena, ); print('Valor numérico: $valorPrueba'); print('Cadena: $cadena'); print('Objeto: ${pelota.id} -- ${pelota.peso}'); }
- Después de ejecutar la función:
valorPruebasigue valiendo5.cadenasigue conteniendo'VIEJA CADENA'.- Las propiedades del objeto
pelotasí han cambiado.
- Por tanto, no es correcto decir que Dart pasa unos tipos por valor y otros por referencia. Todos los argumentos se pasan por valor; cuando trabajamos con objetos, el valor copiado es una referencia al objeto.
Parámetros opcionales posicionales
- Podemos definir parámetros posicionales opcionales escribiéndolos entre corchetes
[ ].
void imprimirMensaje([String mensaje = 'Nada enviado']) { print(mensaje); } void main() { imprimirMensaje(); imprimirMensaje('Nuevo mensaje'); }
- En la primera llamada no enviamos ningún valor, por lo que el parámetro utiliza su valor por defecto.
- Si el parámetro admite valores nulos, no es necesario establecer un valor por defecto:
void imprimirMensaje([String? mensaje]) { print(mensaje); } void main() { imprimirMensaje(); imprimirMensaje('Nuevo mensaje'); }
- Si no enviamos ningún valor,
mensajetendrá el valornull.
- Si una función tiene parámetros posicionales obligatorios y opcionales, los opcionales deben aparecer al final:
void imprimirMensaje(int numero, [String? mensaje]) { print('$numero - $mensaje'); }
Parámetros con nombre
- Dart permite definir parámetros con nombre.
- Para ello se escriben los parámetros entre llaves
{ }.
void imprimirMensaje({ String? mensaje, }) { print(mensaje); }
- Al realizar la llamada indicamos el nombre del parámetro:
void main() { imprimirMensaje( mensaje: 'Hola', ); }
- Una ventaja de los parámetros con nombre es que podemos indicar los argumentos por su nombre, haciendo las llamadas más fáciles de leer.
Parámetros con nombre opcionales
- Los parámetros con nombre son opcionales por defecto.
- Si el parámetro puede valer
null, podemos declararlo como nullable:
void imprimirMensaje1({ String? mensaje, }) { print(mensaje); } void main() { imprimirMensaje1(); imprimirMensaje1( mensaje: 'Hola', ); }
- Otra posibilidad es proporcionar un valor por defecto:
void imprimirMensaje2({ String mensaje = 'Valor por defecto', }) { print(mensaje); } void main() { imprimirMensaje2(); imprimirMensaje2( mensaje: 'Nuevo mensaje', ); }
Parámetros con nombre obligatorios
- Podemos indicar que un parámetro con nombre es obligatorio utilizando la palabra clave
required.
void imprimirMensaje({ required String mensaje, }) { print(mensaje); } void main() { imprimirMensaje( mensaje: 'Mensaje', ); }
- En este caso estamos obligados a enviar el argumento correspondiente a
mensaje.
- Como el parámetro es obligatorio y su tipo es
String, no necesitamos asignarle un valor por defecto ni permitir que seanull.
Combinación de parámetros posicionales y con nombre
- Podemos combinar parámetros posicionales y parámetros con nombre.
- Los parámetros posicionales se escriben antes que los parámetros con nombre:
void imprimirMensaje( int numero, { String? mensaje, } ) { print( 'El número es $numero y el mensaje es $mensaje', ); } void main() { imprimirMensaje( 10, mensaje: 'Mensaje', ); }
Funciones flecha o arrow functions
- Cuando una función está formada por una única expresión, podemos utilizar la sintaxis de función flecha mediante el operador
=>.
- Por ejemplo, la siguiente función:
double sumar(double a, double b) { return a + b; }
- puede escribirse como:
double sumar(double a, double b) => a + b;
- Después de
=>se escribe una expresión.
- El resultado de dicha expresión será el valor devuelto por la función cuando esta tenga un tipo de retorno distinto de
void.
double calcularMedia( double num1, double num2, ) => (num1 + num2) / 2;
- También podemos emplearla con una expresión que no produzca un valor útil, como una llamada a
print:
void imprimirMediaNumero({ required int num1, required int num2, }) => print((num1 + num2) / 2); void main() { imprimirMediaNumero( num1: 10, num2: 15, ); }
- Si necesitamos ejecutar varias instrucciones, debemos emplear un bloque de código con llaves:
void mostrarDatos(int numero) { print('Número recibido:'); print(numero); }
Funciones anónimas
- Una función anónima es una función que no tiene un nombre asociado.
- Las funciones anónimas son especialmente útiles como argumentos de métodos que esperan recibir otra función.
- Este tipo de métodos aparece frecuentemente cuando trabajamos con colecciones y también en Flutter mediante callbacks.
- Una función anónima puede tener un bloque de código:
(elemento) { print(elemento); }
- Si únicamente necesitamos una expresión, podemos emplear la sintaxis flecha:
(elemento) => print(elemento)
Funciones anónimas y forEach
- El método
forEachde unIterableespera recibir una función que será ejecutada una vez por cada elemento.
- Por ejemplo:
void main() { final lista = <int>[1, 2, 3, 4, 5, 6]; lista.forEach((elemento) { print('Accediendo al elemento $elemento'); }); }
- El parámetro
elementorepresenta, en cada ejecución, uno de los elementos de la lista.
- Podemos expresar lo mismo mediante una función flecha:
void main() { final lista = <int>[1, 2, 3, 4, 5, 6]; lista.forEach( (elemento) => print( 'Accediendo al elemento $elemento', ), ); }
Nota importante:
- Aunque estas formas son válidas y sirven para comprender el funcionamiento de las funciones anónimas, para realizar un recorrido sencillo de un
Iterable, Dart recomienda normalmente utilizarfor...inen lugar deforEachcon una función anónima.
- Por ejemplo:
void main() { final lista = <int>[1, 2, 3, 4, 5, 6]; for (final elemento in lista) { print( 'Accediendo al elemento $elemento', ); } }
Pasar una función existente como parámetro
- Cuando un método espera una función como parámetro, no siempre tenemos que crear una función anónima.
- Podemos pasar una función que ya esté definida:
void recorrerConjunto(String elemento) { print(elemento); } void main() { final conjunto = <String>{ 'uno', 'dos', 'tres', }; conjunto.forEach(recorrerConjunto); }
- Fijarse en que escribimos:
conjunto.forEach(recorrerConjunto);
- y no:
conjunto.forEach(recorrerConjunto());
- No queremos ejecutar
recorrerConjuntoen ese momento. Queremos proporcionar la función aforEachpara que sea este método quien la ejecute posteriormente una vez por cada elemento.
- La función que enviemos debe ser compatible con el tipo de función que espera el método.
- En este ejemplo, el conjunto contiene elementos de tipo
String, por lo que la función recibe unStringy devuelvevoid:
void recorrerConjunto(String elemento) { print(elemento); }
Tear-offs
- Cuando hacemos referencia a una función o método sin utilizar los paréntesis de llamada, Dart crea lo que se denomina un tear-off.
- Por ejemplo:
conjunto.forEach(recorrerConjunto);
recorrerConjuntorepresenta la función que queremos proporcionar aforEach.
- Dart recomienda no crear una función anónima innecesaria cuando podemos pasar directamente una función existente.
- Por tanto, en lugar de:
lista.forEach( (elemento) => gestionarElemento(elemento), );
- es preferible:
lista.forEach(gestionarElemento);
- Incluso podemos proporcionar directamente una función existente de Dart si es compatible con los parámetros esperados:
lista.forEach(print);
Diferentes formas de proporcionar una función a forEach
- Veamos las diferentes posibilidades:
void gestionarElemento(int elemento) { print('Elemento: $elemento'); } void main() { final lista = <int>[1, 2, 3, 4, 5, 6]; // Función anónima con bloque. lista.forEach((elemento) { print('Función anónima: $elemento'); }); // Función anónima flecha. lista.forEach( (elemento) => print('Función flecha: $elemento'), ); // Función previamente definida. lista.forEach(gestionarElemento); // Función existente compatible. lista.forEach(print); }
- Las dos primeras formas son útiles para comprender cómo funcionan las funciones anónimas.
- Sin embargo, si únicamente queremos recorrer un
Iterable, normalmente emplearemos:
for (final elemento in lista) { print(elemento); }
- Si queremos ejecutar exactamente una función existente para cada elemento,
forEaches apropiado:
lista.forEach(gestionarElemento); lista.forEach(print);
Funciones que devuelven funciones
- En Dart, las funciones son objetos. Esto significa que una función también puede devolver otra función.
- Veamos un ejemplo:
void Function(int) crearFuncion() { return (int elemento) { print('Número: $elemento'); }; } void main() { final lista = <int>[1, 2, 3]; lista.forEach(crearFuncion()); }
- En este caso ocurre algo diferente al ejemplo anterior.
- Primero se ejecuta:
crearFuncion()
- Esta función devuelve otra función compatible con la función que espera
forEach.
- Por ese motivo, en este caso sí aparecen los paréntesis después de
crearFuncion.
- Este tipo de funciones recibe el nombre de funciones de orden superior. También están relacionadas con el concepto de closures, que veremos cuando sea necesario.
forEach en mapas
- Los objetos de tipo
Maptambién disponen del métodoforEach.
- En este caso, la función recibe dos parámetros:
- La clave.
- El valor asociado a dicha clave.
void main() { final mapa = <String, double>{ 'base': 1.84, 'alero': 1.95, 'pivot': 2.10, 'escolta': 1.90, }; mapa.forEach((clave, valor) { print( 'La clave $clave tiene el valor $valor', ); }); }
- En el caso de
Map.forEach, el empleo de una función anónima es totalmente apropiado.
- Si queremos recorrer solamente las claves o solamente los valores, podemos emplear
for...in:
void main() { final mapa = <String, double>{ 'base': 1.84, 'alero': 1.95, 'pivot': 2.10, 'escolta': 1.90, }; for (final clave in mapa.keys) { print('Clave: $clave'); } for (final valor in mapa.values) { print('Valor: $valor'); } }
Recorrido de conjuntos
- Los conjuntos (
Set) son objetosIterable, por lo que para un recorrido sencillo podemos utilizarfor...in:
void main() { final conjunto = <String>{ 'Ángel', 'Luis', 'Ana', 'María', }; for (final elemento in conjunto) { print(elemento); } }
- También podemos utilizar
forEachsi proporcionamos directamente una función existente:
void main() { final conjunto = <String>{ 'Ángel', 'Luis', 'Ana', 'María', }; conjunto.forEach(print); }
Método where
- El método
wherepermite filtrar los elementos de unIterable.
- Recibe como parámetro una función que debe devolver un valor booleano:
(elemento) => condición
- Si la función devuelve
true, el elemento formará parte del resultado.
- Si devuelve
false, el elemento será descartado.
- Por ejemplo:
void main() { final numeros = <int>[ 1, 2, 3, 4, 5, 6, ]; final pares = numeros.where( (numero) => numero.isEven, ); print(pares); }
wheredevuelve unIterable.
- Si queremos obtener una lista:
final pares = numeros .where((numero) => numero.isEven) .toList();
- Si queremos obtener un conjunto:
final pares = numeros .where((numero) => numero.isEven) .toSet();
Método every
- El método
everypermite comprobar si todos los elementos de unIterablecumplen una determinada condición.
- Devuelve un valor de tipo
bool.
void main() { final numeros = <int>[ 10, 20, 30, 40, ]; final todosPositivos = numeros.every( (numero) => numero > 0, ); print(todosPositivos); }
- El resultado será
trueúnicamente si todos los elementos cumplen la condición.
Método map aplicado a List y Set
- El método
mappermite transformar los elementos de unIterable.
- Por cada elemento de la colección original se ejecuta una función que genera un nuevo valor.
- Como resultado obtenemos un nuevo
Iterable.
- Por ejemplo:
void main() { final numeros = <int>[ 1, 2, 3, 4, ]; final cuadrados = numeros.map( (numero) => numero * numero, ); print(cuadrados); }
- Habitualmente convertiremos el resultado a una lista mediante
toList():
final cuadrados = numeros .map((numero) => numero * numero) .toList();
- Este método es especialmente útil en Flutter, ya que permite crear una colección de widgets a partir de una colección de datos.
- Por ejemplo, conceptualmente podemos transformar una lista de cadenas en una lista de objetos:
void main() { final nombres = <String>[ 'Ana', 'Luis', 'María', ]; final mensajes = nombres .map((nombre) => 'Hola $nombre') .toList(); print(mensajes); }
- También podemos aplicar
mapsobre las entradas de unMap:
void main() { final mapa = <String, String>{ 'nombre': 'Ángel', 'apellido1': 'Fernández', 'apellido2': 'González', }; final lista = mapa.entries .map( (entrada) => 'HOLA: ${entrada.value}', ) .toList(); print(lista); }
List.generate
- La clase
Listdispone del constructorList.generate, que permite generar los elementos de una lista mediante una función.
- La función recibe la posición que se está generando:
void main() { final numeros = List<int>.generate( 5, (posicion) => posicion, ); print(numeros); }
- El resultado será:
[0, 1, 2, 3, 4]
- Si queremos generar los números desde 1:
final numeros = List<int>.generate( 5, (posicion) => posicion + 1, );
- Podemos indicar mediante
growable: falseque la lista tenga longitud fija:
final numeros = List<int>.generate( 5, (posicion) => posicion + 1, growable: false, );
Importante:
- Una lista con
growable: falsees una lista de longitud fija. Esto significa que no podemos añadir ni eliminar elementos.
- Sin embargo, esto no significa que la lista sea inmutable. Podemos modificar el valor almacenado en una posición existente.
Ejercicios propuestos
Ejercicio 1:
Crea una función que espere recibir mediante parámetros con nombre una posición y una lista de cadenas, y devuelva el valor de la lista que se encuentra en la posición indicada.
- La lista debe ser enviada obligatoriamente.
- Si no se envía la posición, debe utilizarse la posición
0. - Si la lista no tiene elementos, debe devolver la cadena
'Sin datos'. - Si la posición es negativa o no existe en la lista, debe devolver la cadena
'Índice fuera de rango'. - Llama a la función empleando diferentes valores para comprobar su funcionamiento.
Ejercicio 2:
Crea un mapa de datos en el que se guarde como clave un índice entero y como valor un precio.
- Define inicialmente cuatro entradas en el mapa.
- Añade dos nuevas entradas.
- Borra la última entrada introducida.
- Recorre el mapa empleando
forEach. La función debe mostrar la clave y el precio de todos los platos cuyo precio sea inferior a 5 euros. - Crea una función de nombre
getNumPlatosMasCarosa la que se le pase mediante parámetros con nombre un mapa y un precio. - Los dos parámetros deben ser obligatorios y no admitir valores nulos.
- La función debe devolver cuántos platos tienen un precio superior al indicado.
- Emplea
wherepara realizar el filtrado. - Llama a la función para comprobar que funciona.
Ejercicio 3:
Crea un conjunto de nombre equipo que contenga los valores:
Base Alero Pivot Escolta
Crea una función que reciba como parámetros el conjunto y una cadena a buscar.
- Emplea el método
where. - La función debe mostrar los elementos del conjunto que contengan la cadena indicada.
- Recorre el resultado empleando
for...in. - Realiza una búsqueda con la cadena
'as'. - Realiza otra búsqueda con la cadena
's'.
- Emplea el método
Ejercicio 4:
Define una lista de números decimales con los siguientes valores:
10.0, 11.22, 15.0, 7.0, 3.44
Crea una función de nombre sonMayores:
- Debe recibir mediante parámetros con nombre un número y una lista de números decimales.
- La lista debe ser obligatoria.
- Si no se envía el número, debe utilizarse el valor
0. - Emplea el método
everypara comprobar si todos los elementos de la lista son mayores que el número indicado. - La función debe devolver la cadena
'SI'o'NO'. - Llama a la función empleando los valores
2y12. - Muestra una frase con el formato:
Todos los números de la lista son mayores a XX: YY
- siendo
XXel número utilizado eYYel resultadoSIoNO.
Ejercicio 5:
Emplea List.generate para realizar los siguientes ejercicios:
- Genera una lista de 100 números enteros aleatorios comprendidos entre 1 y 100. Emplea la clase Random.
- Genera una lista con los números comprendidos entre 1 y 100 y haz que tenga longitud fija, de forma que no puedan añadirse ni eliminarse elementos.
- Dado un número, genera una lista con los cuadrados de todos los números comprendidos entre 1 y dicho número. En este caso, la lista debe permitir añadir nuevos elementos.
Ejercicio adicional:
Investiga otros métodos disponibles en List, Set, Map o Iterable que reciban una función como parámetro.
- Busca varios ejemplos.
- Indica qué tipo de función espera cada método.
- Realiza un pequeño ejemplo de uso.
- Algunos métodos que puedes investigar son
any,firstWhere,fold,removeWhereoupdateAll.
Soluciones de los ejercicios propuestos
Solución Ejercicio 1:
String devolverElementoLista({ int posicion = 0, required List<String> lista, }) { if (lista.isEmpty) { return 'Sin datos'; } if ( posicion < 0 || posicion >= lista.length ) { return 'Índice fuera de rango'; } return lista[posicion]; } void main() { final lista = <String>[ 'Uno', 'Dos', 'Tres', 'Cuatro', ]; print( devolverElementoLista( lista: lista, ), ); print( devolverElementoLista( posicion: 2, lista: lista, ), ); print( devolverElementoLista( lista: lista, posicion: 4, ), ); print( devolverElementoLista( posicion: -1, lista: lista, ), ); }
Solución Ejercicio 2:
int getNumPlatosMasCaros({ required Map<int, double> mapa, required double precio, }) { return mapa.values .where( (precioMapa) => precioMapa > precio, ) .length; } void main() { final precios = <int, double>{ 1: 10.34, 2: 3.24, 3: 24.99, 4: 17.35, }; precios.addAll({ 5: 9.23, 6: 2.22, }); print(precios); final ultimaClave = precios.keys.last; precios.remove(ultimaClave); precios.forEach((clave, valor) { if (valor < 5) { print( 'La clave $clave tiene un precio de $valor', ); } }); const precioReferencia = 10.0; final numPlatos = getNumPlatosMasCaros( mapa: precios, precio: precioReferencia, ); print( 'Número de platos con un precio superior ' 'a $precioReferencia euros: $numPlatos', ); }
Solución Ejercicio 3:
void buscarCadena( Set<String> conjunto, String cadena, ) { final encontrados = conjunto.where( (elemento) => elemento.contains(cadena), ); for (final elemento in encontrados) { print('Encontrado: $elemento'); } } void main() { final equipo = <String>{ 'Base', 'Alero', 'Pivot', 'Escolta', }; var cadenaBusqueda = 'as'; print( 'Cadena de búsqueda: $cadenaBusqueda', ); buscarCadena( equipo, cadenaBusqueda, ); cadenaBusqueda = 's'; print( 'Cadena de búsqueda: $cadenaBusqueda', ); buscarCadena( equipo, cadenaBusqueda, ); }
Solución Ejercicio 4:
String sonMayores({ double numero = 0, required List<double> lista, }) => lista.every( (elemento) => elemento > numero, ) ? 'SI' : 'NO'; void main() { final listaNumeros = <double>[ 10.0, 11.22, 15.0, 7.0, 3.44, ]; var numeroReferencia = 2.0; print( 'Todos los números de la lista son mayores ' 'a $numeroReferencia: ' '${sonMayores( numero: numeroReferencia, lista: listaNumeros, )}', ); numeroReferencia = 12.0; print( 'Todos los números de la lista son mayores ' 'a $numeroReferencia: ' '${sonMayores( lista: listaNumeros, numero: numeroReferencia, )}', ); }
Solución Ejercicio 5:
import 'dart:math'; void main() { final random = Random(); // 100 números aleatorios entre 1 y 100. final numerosAleatorios = List<int>.generate( 100, (_) => random.nextInt(100) + 1, ); print(numerosAleatorios); // Números desde 1 hasta 100. // Lista de longitud fija. final numerosSecuenciales = List<int>.generate( 100, (posicion) => posicion + 1, growable: false, ); print(numerosSecuenciales); // Cuadrados desde 1 hasta numero. // La lista permite añadir elementos. const numero = 10; final numerosAlCuadrado = List<int>.generate( numero, (posicion) { final valor = posicion + 1; return valor * valor; }, growable: true, ); print(numerosAlCuadrado); numerosAlCuadrado.add(121); print(numerosAlCuadrado); }
Enlace a la página principal de la UD2
Enlace a la página principal del curso
```