import "dart:collection"; import "dart:math" as DM; // Bienvenido a Aprende Dart en 15 minutos. http://www.dartlang.org/ // Este es un tutorial ejecutable. Puedes ejecutarlo con Dart o en // el sitio de ¡Try Dart! solo copiando y pegando en http://try.dartlang.org/ // La declaración de función y de método tienen el mismo aspecto. // Las funciones pueden estar anidadas. // La declaración toma la forma name() {} o name() => expresionEnUnaLinea; // La declaración de la función de flecha gorda, tiene un retorno implícito // para el resultado de la expresión. example1() { nested1() { nested2() => print("example1 anidado 1 anidado 2"); nested2(); } nested1(); } // Las funciones anónimas no incluyen un nombre. example2() { nested1(fn) { fn(); } nested1(() => print("example2 anidado 1")); } // Cuando se declara un parámetro de función, la declaración puede incluir el // número de parámetros que toma la función especificando los nombres de los // parámetros que lleva. example3() { planA(fn(informSomething)) { fn("example3 plan A"); } planB(fn) { // O no declarar el número de parámetros. fn("example3 plan B"); } planA((s) => print(s)); planB((s) => print(s)); } // Las funciones tienen acceso de cierre a variables externas. var example4Something = "Example4 anidado 1"; example4() { nested1(fn(informSomething)) { fn(example4Something); } nested1((s) => print(s)); } // La declaración de la clase con un método sayIt, el cual también tiene acceso de cierre // a la variable exterior como si fuera una función como se ha visto antes. var example5method = "example5 sayIt"; class Example5Class { sayIt() { print(example5method); } } example5() { // Crear una instancia anónima de Example5Class y la llamada del método sayIt new Example5Class().sayIt(); } // La declaración de clase toma la forma NombreDeClase { [cuerpoDeClase] }. // Donde cuerpoDeClase puede incluir métodos de instancia y variables, pero también // métodos y variables de clase. class Example6Class { var instanceVariable = "Example6 variable de instancia"; sayIt() { print(instanceVariable); } } example6() { new Example6Class().sayIt(); } // Los métodos y variables de clase son declarados con términos "static". class Example7Class { static var classVariable = "Example7 variable de clase"; static sayItFromClass() { print(classVariable); } sayItFromInstance() { print(classVariable); } } example7() { Example7Class.sayItFromClass(); new Example7Class().sayItFromInstance(); } // Las literales son geniales, pero hay una restricción para lo que pueden ser las literales // fuera de los cuerpos de función/método. Literales en el ámbito exterior de clase // o fuera de clase tienen que ser constantes. Las cadenas de caracteres y los números // son constantes por defecto. Pero los arreglos y mapas no lo son. // Ellos pueden hacerse constante anteponiendo en la declaración el término "const". var example8Array = const ["Example8 arreglo constante"], example8Map = const {"algunaKey": "Example8 mapa constante"}; example8() { print(example8Array[0]); print(example8Map["algunaKey"]); } // Los bucles en Dart toman la forma estándar para for () {} o ciclos while () {} , // ligeramente más moderno for (.. in ..) {}, o llamadas funcionales con muchas // características soportadas, comenzando con forEach. var example9Array = const ["a", "b"]; example9() { for (var i = 0; i < example9Array.length; i++) { print("example9 ciclo for '${example9Array[i]}'"); } var i = 0; while (i < example9Array.length) { print("example9 ciclo while '${example9Array[i]}'"); i++; } for (var e in example9Array) { print("example9 ciclo for-in '${e}'"); } example9Array.forEach((e) => print("example9 ciclo forEach '${e}'")); } // Para recorrer los caracteres de una cadena o para extraer una subcadena. var example10String = "ab"; example10() { for (var i = 0; i < example10String.length; i++) { print("example10 Recorrido de caracteres en la cadena '${example10String[i]}'"); } for (var i = 0; i < example10String.length; i++) { print("example10 ciclo de subcadena '${example10String.substring(i, i + 1)}'"); } } // Formato de números Int y double son soportados. example11() { var i = 1 + 320, d = 3.2 + 0.01; print("example11 int ${i}"); print("example11 double ${d}"); } // DateTime ofrece aritmética de fecha/hora. example12() { var now = new DateTime.now(); print("example12 ahora '${now}'"); now = now.add(new Duration(days: 1)); print("example12 manana '${now}'"); } // Expresiones regulares son soportadas. example13() { var s1 = "alguna cadena", s2 = "alguna", re = new RegExp("^s.+?g\$"); match(s) { if (re.hasMatch(s)) { print("example13 regexp embona '${s}'"); } else { print("example13 regexp no embona '${s}'"); } } match(s1); match(s2); } // Las expresiones booleanas admiten conversiones implícitas y tipos dinámicos. example14() { var a = true; if (a) { print("true, a is $a"); } a = null; if (a) { print("true, a es $a"); } else { print("false, a es $a"); // corre aquí } // el tipado dinámico null puede convertirse a bool var b; // b es de tipo dinámico b = "abc"; try { if (b) { print("true, b es $b"); } else { print("false, b es $b"); } } catch (e) { print("error, b es $b"); // esto podría ser ejecutado pero consiguió error } b = null; if (b) { print("true, b es $b"); } else { print("false, b es $b"); // corre aquí } // tipado estático null no puede ser convertido a bool var c = "abc"; c = null; // compilación fallida // if (c) { // print("true, c is $c"); // } else { // print("false, c is $c"); // } } // try/catch/finally y throw son utilizados para el manejo de excepciones. // throw toma cualquier objeto como parámetro; example15() { try { try { throw "Algun error inesperado."; } catch (e) { print("example15 una excepcion: '${e}'"); throw e; // Re-throw } } catch (e) { print("example15 atrapa la excepcion que ha sido relanzada: '${e}'"); } finally { print("example15 aún ejecuta finally"); } } // Para ser eficiente cuando creas una cadena larga dinámicamente, usa // StringBuffer. O podrías unir un arreglo de cadena de caracteres. example16() { var sb = new StringBuffer(), a = ["a", "b", "c", "d"], e; for (e in a) { sb.write(e); } print("example16 cadena de caracteres dinamica creada con " "StringBuffer '${sb.toString()}'"); print("example16 union de arreglo de cadena de caracteres '${a.join()}'"); } // Las cadenas de caracteres pueden ser concatenadas contando solo // con literales una después de la otra sin algún otro operador necesario. example17() { print("example17 " "concatenar " "cadenas " "asi"); } // Las cadenas de caracteres utilizan comilla simple o comillas dobles como delimitadores // sin ninguna diferencia entre ambas. Esto proporciona flexibilidad que puede ser efectiva // para evitar la necesidad de 'escapar' el contenido. Por ejemplo, // las dobles comillas de los atributos HTML. example18() { print('Example18 ' "Don't can't I'm Etc" ''); } // Las cadenas de caracteres con triple comilla simple o triple comillas dobles // dividen múltiples lineas e incluyen como delimitador el salto de línea. example19() { print('''Example19 Example19 Don't can't I'm Etc Example19 '''); } // Las cadenas de caracteres cuentan con una extraordinaria característica // para la interpolación de caracteres utilizando el operador $ // Con $ { [expresion] }, devolvemos la expresion interpolada. // $ seguido por el nombre de una variable interpola el contenido de dicha variable. // $ puede ser escapado con \$ para solo agregarlo a la cadena. example20() { var s1 = "'\${s}'", s2 = "'\$s'"; print("Example20 \$ interpolation ${s1} or $s2 works."); } // Hasta ahora no hemos declarado ningún tipo de dato y los programas // han funcionado bien. De hecho, los tipos no se toman en cuenta durante // el tiempo de ejecución. // Los tipos incluso pueden estar equivocados y al programa todavía se le dará // el beneficio de la duda y se ejecutará como si los tipos no importaran. // Hay un parámetro de tiempo de ejecución que comprueba los errores de tipo que es // el modo de verificación, el cuál es útil durante el tiempo de desarrollo, // pero que también es más lento debido a la comprobación adicional y, por lo tanto // se evita durante el tiempo de ejecución de la implementación. class Example21 { List _names; Example21() { _names = ["a", "b"]; } List get names => _names; set names(List list) { _names = list; } int get length => _names.length; void add(String name) { _names.add(name); } } void example21() { Example21 o = new Example21(); o.add("c"); print("example21 nombres '${o.names}' y longitud '${o.length}'"); o.names = ["d", "e"]; print("example21 nombres '${o.names}' y longitud '${o.length}'"); } // La herencia de clases toma la forma NombreDeClase extends OtraClase {}. class Example22A { var _name = "¡Algun Nombre!"; get name => _name; } class Example22B extends Example22A {} example22() { var o = new Example22B(); print("example22 herencia de clase '${o.name}'"); } // La mezcla de clases también esta disponible y toman la forma de // NombreDeClase extends AlgunaClase with OtraClase {}. // Es necesario extender de alguna clase para poder mezclar con otra. // La clase de plantilla de mixin no puede en este momento tener un constructor. // Mixin se utiliza principalmente para compartir métodos con clases distantes, // por lo que la herencia única no interfiere con el código reutilizable. // Mixins se colocan despues de la palabra "with" durante la declaración de la clase. class Example23A {} class Example23Utils { addTwo(n1, n2) { return n1 + n2; } } class Example23B extends Example23A with Example23Utils { addThree(n1, n2, n3) { return addTwo(n1, n2) + n3; } } example23() { var o = new Example23B(), r1 = o.addThree(1, 2, 3), r2 = o.addTwo(1, 2); print("Example23 addThree(1, 2, 3) results in '${r1}'"); print("Example23 addTwo(1, 2) results in '${r2}'"); } // El método constructor de la clase utiliza el mismo nombre de la clase // y toma la forma de AlgunaClase() : super() {}, donde la parte ": super()" // es opcional y es utilizado para delegar parametros constantes // al método constructor de la clase padre o super clase. class Example24A { var _value; Example24A({value: "algunValor"}) { _value = value; } get value => _value; } class Example24B extends Example24A { Example24B({value: "algunOtroValor"}) : super(value: value); } example24() { var o1 = new Example24B(), o2 = new Example24B(value: "aunMas"); print("example24 llama al método super desde el constructor '${o1.value}'"); print("example24 llama al método super desde el constructor '${o2.value}'"); } // Hay un atajo para configurar los parámetros del constructor en el caso de clases más simples. // Simplemente use el prefijo this.nombreParametro y establecerá el parámetro // en una variable de instancia del mismo nombre. class Example25 { var value, anotherValue; Example25({this.value, this.anotherValue}); } example25() { var o = new Example25(value: "a", anotherValue: "b"); print("example25 atajo para el constructor '${o.value}' y " "'${o.anotherValue}'"); } // Los parámetros con nombre están disponibles cuando se declaran entre {}. // El orden de los parámetros puede ser opcional cuando se declara entre {}. // Los parámetros pueden hacerse opcionales cuando se declaran entre []. example26() { var _name, _surname, _email; setConfig1({name, surname}) { _name = name; _surname = surname; } setConfig2(name, [surname, email]) { _name = name; _surname = surname; _email = email; } setConfig1(surname: "Doe", name: "John"); print("example26 name '${_name}', surname '${_surname}', " "email '${_email}'"); setConfig2("Mary", "Jane"); print("example26 name '${_name}', surname '${_surname}', " "email '${_email}'"); } // Las variables declaradas con final solo se pueden establecer una vez. // En el caso de las clases, las variables de instancia final se pueden establecer // a través de la constante del parámetro constructor. class Example27 { final color1, color2; // Un poco de flexibilidad para establecer variables de instancia finales con la sintaxis // que sigue a : Example27({this.color1, color2}) : color2 = color2; } example27() { final color = "orange", o = new Example27(color1: "lilac", color2: "white"); print("example27 color es '${color}'"); print("example27 color es '${o.color1}' y '${o.color2}'"); } // Para importar una librería utiliza la palabra reservada import "rutaLibrería" o si es una biblioteca central, // import "dart:NombreLibrería". También está el "pub" administrador de paquetes con // su propia convensión import "package:NombrePaquete". // Ve import "dart:collection"; al inicio. Las importaciones deben venir antes // de la delcaración de algún otro código. IterableBase proviene de dart:collection. class Example28 extends IterableBase { var names; Example28() { names = ["a", "b"]; } get iterator => names.iterator; } example28() { var o = new Example28(); o.forEach((name) => print("example28 '${name}'")); } // Para el control de flujo tenemos: // * estandard switch // * if-else if-else y el operador ternario ..?..:.. // * closures y funciones anonimas // * sentencias break, continue y return example29() { var v = true ? 30 : 60; switch (v) { case 30: print("example29 sentencia switch"); break; } if (v < 30) { } else if (v > 30) { } else { print("example29 sentencia if-else"); } callItForMe(fn()) { return fn(); } rand() { v = new DM.Random().nextInt(50); return v; } while (true) { print("example29 callItForMe(rand) '${callItForMe(rand)}'"); if (v != 30) { break; } else { continue; } // Nunca llega aquí. } } // La sentencia int.parse, convierte de tipo double a int, o simplemente mantener int cuando se dividen los números // utilizando ~/ como operación. Vamos a jugar un juego de adivinanzas también. example30() { var gn, tooHigh = false, n, n2 = (2.0).toInt(), top = int.parse("123") ~/ n2, bottom = 0; top = top ~/ 6; gn = new DM.Random().nextInt(top + 1); // +1 porque nextInt top es exclusivo print("example30 Adivina un número entre 0 y ${top}"); guessNumber(i) { if (n == gn) { print("example30 ¡Adivinaste correctamente! El número es ${gn}"); } else { tooHigh = n > gn; print("example30 Número ${n} es demasiado " "${tooHigh ? 'high' : 'low'}. Intenta nuevamente"); } return n == gn; } n = (top - bottom) ~/ 2; while (!guessNumber(n)) { if (tooHigh) { top = n - 1; } else { bottom = n + 1; } n = bottom + ((top - bottom) ~/ 2); } } // Los programas tienen un solo punto de entrada en la función principal. // No se espera que se ejecute nada en el ámbito externo antes de que un programa // comience a funcionar con su función principal. // Esto ayuda con una carga más rápida e incluso con una carga lenta // de lo que necesita el programa para iniciar. main() { print("Learn Dart in 15 minutes!"); [example1, example2, example3, example4, example5, example6, example7, example8, example9, example10, example11, example12, example13, example14, example15, example16, example17, example18, example19, example20, example21, example22, example23, example24, example25, example26, example27, example28, example29, example30 ].forEach((ef) => ef()); }