diff options
Diffstat (limited to 'es-es')
-rw-r--r-- | es-es/c++-es.html.markdown | 126 | ||||
-rw-r--r-- | es-es/elixir-es.html.markdown | 457 |
2 files changed, 520 insertions, 63 deletions
diff --git a/es-es/c++-es.html.markdown b/es-es/c++-es.html.markdown index 07c8bc03..bd1ad07c 100644 --- a/es-es/c++-es.html.markdown +++ b/es-es/c++-es.html.markdown @@ -20,11 +20,11 @@ fue diseñado para - soportar programación orientada a objetos - soportar programación genérica -Aunque su sintaxis puede ser más difícil o compleja que los nuevos lenguajes, -es ampliamente utilizado, ya que compila instrucciones nativas que pueden ser -directamente ejecutadas por el procesador y ofrece un estricto control sobre -el hardware (como C), mientras ofrece características de alto nivel como -genericidad, excepciones, y clases. Esta combinación de velocidad y +Aunque su sintaxis puede ser más difícil o compleja que los nuevos lenguajes, +es ampliamente utilizado, ya que compila instrucciones nativas que pueden ser +directamente ejecutadas por el procesador y ofrece un estricto control sobre +el hardware (como C), mientras ofrece características de alto nivel como +genericidad, excepciones, y clases. Esta combinación de velocidad y funcionalidad hace de C ++ uno de los lenguajes de programación más utilizados. ```c++ @@ -32,22 +32,22 @@ funcionalidad hace de C ++ uno de los lenguajes de programación más utilizados // Comparación con C //////////////////// -// C ++ es _casi_ un superconjunto de C y comparte su sintaxis básica para las +// C ++ es _casi_ un superconjunto de C y comparte su sintaxis básica para las // declaraciones de variables, tipos primitivos y funciones. -// Al igual que en C, el punto de entrada de tu programa es una función llamada -// main con un retorno de tipo entero. +// Al igual que en C, el punto de entrada de tu programa es una función llamada +// main con un retorno de tipo entero. // Este valor sirve como código de salida del programa. // Mira http://en.wikipedia.org/wiki/Exit_status para mayor información. int main(int argc, char** argv) { - // Los argumentos de la línea de comandos se pasan por argc y argv de la + // Los argumentos de la línea de comandos se pasan por argc y argv de la // misma manera que en C. - // argc indica el número de argumentos, - // y argv es un arreglo de strings de estilo C (char*) + // argc indica el número de argumentos, + // y argv es un arreglo de strings de estilo C (char*) // representando los argumentos. // El primer argumento es el nombre con el que el programa es llamado. - // argc y argv pueden omitirse si no te preocupan los argumentos, + // argc y argv pueden omitirse si no te preocupan los argumentos, // dejando la definición de la función como int main () // Un estado de salida 0 indica éxito. @@ -72,7 +72,7 @@ void func(); // función que puede aceptar cualquier número de argumentos // Use nullptr en lugar de NULL en C++ int* ip = nullptr; -// Las cabeceras (headers) estándar de C están disponibles en C ++, +// Las cabeceras (headers) estándar de C están disponibles en C ++, // pero tienen el prefijo "c" y no tienen sufijo .h. #include <cstdio> @@ -109,7 +109,7 @@ int main() // Argumentos de función por defecto //////////////////////////////////// -// Puedes proporcionar argumentos por defecto para una función si no son +// Puedes proporcionar argumentos por defecto para una función si no son // proporcionados por quien la llama. void doSomethingWithInts(int a = 1, int b = 4) @@ -134,7 +134,7 @@ void invalidDeclaration(int a = 1, int b) // Error! // Espacios de nombre ///////////////////// -// Espacios de nombres proporcionan ámbitos separados para variable, función y +// Espacios de nombres proporcionan ámbitos separados para variable, función y // otras declaraciones. // Los espacios de nombres se pueden anidar. @@ -162,8 +162,8 @@ void foo() int main() { - // Incluye todos los símbolos del espacio de nombre Second en el ámbito - // actual. Tenga en cuenta que simplemente foo() no funciona, ya que ahora + // Incluye todos los símbolos del espacio de nombre Second en el ámbito + // actual. Tenga en cuenta que simplemente foo() no funciona, ya que ahora // es ambigua si estamos llamando a foo en espacio de nombres Second o en // el nivel superior. using namespace Second; @@ -254,7 +254,7 @@ const string& barRef = bar; // Crea una referencia constante a bar. // modificados. barRef += ". Hola!"; // Error, referencia constante no puede ser modificada. -// Sidetrack: Antes de hablar más sobre referencias, hay que introducir un +// Sidetrack: Antes de hablar más sobre referencias, hay que introducir un // concepto llamado objeto temporal. Supongamos que tenemos el siguiente código: string tempObjectFun() { ... } string retVal = tempObjectFun(); @@ -267,16 +267,16 @@ string retVal = tempObjectFun(); // El objeto devuelto se llama objeto temporal. Objetos temporales son // creados cada vez que una función devuelve un objeto, y es destruido en el // fin de la evaluación de la expresión que encierra (Bueno, esto es lo que la -// norma dice, pero los compiladores están autorizados a cambiar este -// comportamiento. Busca "return value optimization" para ver mas detalles). +// norma dice, pero los compiladores están autorizados a cambiar este +// comportamiento. Busca "return value optimization" para ver mas detalles). // Así que en este código: foo(bar(tempObjectFun())) // Suponiendo que foo y bar existen, el objeto retornado de tempObjectFun es // pasado al bar, y se destruye antes de llamar foo. -// Ahora, de vuelta a las referencias. La excepción a la regla "en el extremo -// de la expresión encerrada" es si un objeto temporal se une a una +// Ahora, de vuelta a las referencias. La excepción a la regla "en el extremo +// de la expresión encerrada" es si un objeto temporal se une a una // referencia constante, en cuyo caso su vida se extiende al ámbito actual: void constReferenceTempObjectFun() { @@ -287,7 +287,7 @@ void constReferenceTempObjectFun() { } // Otro tipo de referencia introducida en C ++ 11 es específicamente para -// objetos temporales. No se puede tener una variable de este tipo, pero tiene +// objetos temporales. No se puede tener una variable de este tipo, pero tiene // prioridad en resolución de sobrecarga: void someFun(string& s) { ... } // Referencia regular @@ -302,7 +302,7 @@ someFun(tempObjectFun()); // Llama la versión con referencia temporal basic_string(const basic_string& other); basic_string(basic_string&& other); -// La idea es que si estamos construyendo una nueva cadena de un objeto temporal +// La idea es que si estamos construyendo una nueva cadena de un objeto temporal // (que va a ser destruido pronto de todos modos), podemos tener un constructor // mas eficiente que "rescata" partes de esa cadena temporal. Usted verá este // Concepto denominado "movimiento semántico". @@ -341,13 +341,13 @@ public: // Funciones que no modifican el estado del objeto // Deben marcarse como const. // Esto le permite llamarlas si se envia una referencia constante al objeto. - // También tenga en cuenta que las funciones deben ser declaradas - // explícitamente como _virtual_ para que sea reemplazada en las clases + // También tenga en cuenta que las funciones deben ser declaradas + // explícitamente como _virtual_ para que sea reemplazada en las clases // derivadas. - // Las funciones no son virtuales por defecto por razones de rendimiento. + // Las funciones no son virtuales por defecto por razones de rendimiento. virtual void print() const; - // Las funciones también se pueden definir en el interior + // Las funciones también se pueden definir en el interior // del cuerpo de la clase. // Funciones definidas como tales están entre líneas automáticamente. void bark() const { std::cout << name << " barks!\n"; } @@ -358,7 +358,7 @@ public: // (mira abajo) // El destructor debe ser virtual si una clase es dervada desde el; // Si no es virtual, entonces la clase derivada destructor - // No será llamada si el objeto se destruye a través de una referencia de + // No será llamada si el objeto se destruye a través de una referencia de // la clase base o puntero. virtual ~Dog(); @@ -392,7 +392,7 @@ void Dog::print() const Dog::~Dog() { - cout << "Adiós " << name << "\n"; + std::cout << "Adiós " << name << "\n"; } int main() { @@ -410,7 +410,7 @@ class OwnedDog : public Dog { void setOwner(const std::string& dogsOwner); - // Reemplaza el comportamiento de la función de impresión + // Reemplaza el comportamiento de la función de impresión // de todos los OwnedDogs. Mira // http://en.wikipedia.org/wiki/Polymorphism_(computer_science)#Subtyping // Para una introducción más general si no está familiarizado con el @@ -442,7 +442,7 @@ void OwnedDog::print() const // Inicialización y sobrecarga de operadores //////////////////////////////////////////// -// En C ++ se puede sobrecargar el comportamiento +// En C ++ se puede sobrecargar el comportamiento // de los operadores como +, -, *, /, etc. // Esto se hace mediante la definición de una función que es llamada // cada vez que se utiliza el operador. @@ -505,14 +505,14 @@ int main () { // Plantillas (Templates) ///////////////////////// -// Las plantillas en C++ se utilizan sobre todo en la programación genérica, -// a pesar de que son mucho más poderoso que los constructores genéricos -// en otros lenguajes. Ellos también soportan especialización explícita y -// parcial y clases de tipo estilo funcional; de hecho, son un lenguaje +// Las plantillas en C++ se utilizan sobre todo en la programación genérica, +// a pesar de que son mucho más poderoso que los constructores genéricos +// en otros lenguajes. Ellos también soportan especialización explícita y +// parcial y clases de tipo estilo funcional; de hecho, son un lenguaje // funcional Turing-completo incrustado en C ++! -// Empezamos con el tipo de programación genérica que podría estar -// familiarizado. +// Empezamos con el tipo de programación genérica que podría estar +// familiarizado. // Para definir una clase o función que toma un parámetro de tipo: template<class T> class Box { @@ -521,9 +521,9 @@ public: void insert(const T&) { ... } }; -// Durante la compilación, el compilador realmente genera copias de cada -// plantilla con parámetros sustituidos, por lo que la definición completa -// de la clase debe estar presente en cada invocación. +// Durante la compilación, el compilador realmente genera copias de cada +// plantilla con parámetros sustituidos, por lo que la definición completa +// de la clase debe estar presente en cada invocación. // Es por esto que usted verá clases de plantilla definidas // Enteramente en archivos de cabecera. @@ -537,8 +537,8 @@ intBox.insert(123); Box<Box<int> > boxOfBox; boxOfBox.insert(intBox); -// Hasta C++11, había que colocar un espacio entre los dos '>'s, -// de lo contrario '>>' serían analizados como el operador de desplazamiento +// Hasta C++11, había que colocar un espacio entre los dos '>'s, +// de lo contrario '>>' serían analizados como el operador de desplazamiento // a la derecha. @@ -558,9 +558,9 @@ void barkThreeTimes(const T& input) input.bark(); } -// Observe que no se especifica nada acerca de los tipos de parámetros aquí. -// El compilador generará y comprobará cada invocación de la plantilla, -// por lo que la función anterior funciona con cualquier tipo "T" +// Observe que no se especifica nada acerca de los tipos de parámetros aquí. +// El compilador generará y comprobará cada invocación de la plantilla, +// por lo que la función anterior funciona con cualquier tipo "T" // que tenga un método 'bark' constante! @@ -574,12 +574,12 @@ void printMessage() { cout << "Aprende C++ en " << Y << " minutos!" << endl; } -// Y usted puede especializar explícitamente plantillas -// para código más eficiente. -// Por supuesto, la mayor parte del mundo real que utiliza una especialización +// Y usted puede especializar explícitamente plantillas +// para código más eficiente. +// Por supuesto, la mayor parte del mundo real que utiliza una especialización // no son tan triviales como esta. -// Tenga en cuenta que usted todavía tiene que declarar la función (o clase) -// como plantilla incluso si ha especificado de forma explícita todos +// Tenga en cuenta que usted todavía tiene que declarar la función (o clase) +// como plantilla incluso si ha especificado de forma explícita todos // los parámetros. template<> @@ -601,7 +601,7 @@ printMessage<10>(); // Prints "Aprende C++ rapido en solo 10 minutos!" #include <exception> #include <stdexcept> -//Todas las excepciones lanzadas dentro del bloque _try_ pueden ser +//Todas las excepciones lanzadas dentro del bloque _try_ pueden ser // capturados por los siguientes manejadores _catch_. try { // No asignar excepciones en el heap usando _new_. @@ -651,7 +651,7 @@ void doSomethingWithAFile(const char* filename) // (Excepciones son la mejor forma de manejar los fallos, // pero algunos programadores, especialmente los que tienen un fondo C, // estan en desacuerdo sobre la utilidad de las excepciones). -// Ahora tenemos que comprobar cada llamado por fallos y cerrar el manejador +// Ahora tenemos que comprobar cada llamado por fallos y cerrar el manejador // del archivo si se ha producido un problema. bool doSomethingWithAFile(const char* filename) { @@ -716,7 +716,7 @@ void doSomethingWithAFile(const char* filename) // Compare esto con el uso de la clase de flujo de archivos de C++ (fstream) // fstream utiliza su destructor para cerrar el archivo. -// Los destructores son llamados automáticamente +// Los destructores son llamados automáticamente // cuando un objeto queda fuera del ámbito. void doSomethingWithAFile(const std::string& filename) { @@ -734,7 +734,7 @@ void doSomethingWithAFile(const std::string& filename) // 1. No importa lo que pase, // El recurso (en este caso el manejador de archivo) será limpiado. // Una vez que escribes el destructor correctamente, -// Es _imposible_ olvidar cerrar el identificador y permitir +// Es _imposible_ olvidar cerrar el identificador y permitir // fugas del recurso. // 2. Tenga en cuenta que el código es mucho más limpio. // El destructor se encarga de cerrar el archivo detrás de cámaras @@ -743,13 +743,13 @@ void doSomethingWithAFile(const std::string& filename) // Una excepción puede ser lanzado en cualquier lugar de la función // y la limpieza ocurrirá. -// Todo el código idiomático C++ utiliza RAII ampliamente para todos los +// Todo el código idiomático C++ utiliza RAII ampliamente para todos los // recursos. // Otros ejemplos incluyen // - Memoria usando unique_ptr y shared_ptr // - Contenedores (Containers) - la biblioteca estándar linked list, // vector (es decir, array con auto-cambio de tamaño), hash maps, etc. -// Destruimos todos sus contenidos de forma automática +// Destruimos todos sus contenidos de forma automática // cuando quedan fuera del ámbito. // - Mutex utilizando lock_guard y unique_lock @@ -758,9 +758,9 @@ void doSomethingWithAFile(const std::string& filename) // Cosas divertidas ///////////////////// -// Aspectos de C ++ que pueden sorprender a los recién llegados +// Aspectos de C ++ que pueden sorprender a los recién llegados // (e incluso algunos veteranos). -// Esta sección es, por desgracia, salvajemente incompleta; +// Esta sección es, por desgracia, salvajemente incompleta; // C++ es uno de los lenguajes con los que mas facil te disparas en el pie. // Tu puedes sobreescribir métodos privados! @@ -788,13 +788,13 @@ pt2 = nullptr; // Establece pt2 como null. *pt = nullptr; // Esto todavía compila, a pesar de que '*pt' es un bool! // '=' != '=' != '='! -// Llama Foo::Foo(const Foo&) o alguna variante (mira movimientos semanticos) +// Llama Foo::Foo(const Foo&) o alguna variante (mira movimientos semanticos) // copia del constructor. Foo f2; Foo f1 = f2; // Llama Foo::Foo(const Foo&) o variante, pero solo copia el 'Foo' parte de -// 'fooSub'. Cualquier miembro extra de 'fooSub' se descarta. Este +// 'fooSub'. Cualquier miembro extra de 'fooSub' se descarta. Este // comportamiento horrible se llama "Corte de objetos." FooSub fooSub; Foo f1 = fooSub; @@ -809,13 +809,13 @@ class Foo { ... }; vector<Foo> v; for (int i = 0; i < 10; ++i) v.push_back(Foo()); -// La siguiente línea establece el tamaño de v en 0, +// La siguiente línea establece el tamaño de v en 0, // pero los destructores no son llamados y los recursos no se liberan! v.empty(); v.push_back(Foo()); // Nuevo valor se copia en el primer Foo que insertamos -// En verdad destruye todos los valores en v. +// En verdad destruye todos los valores en v. // Consulta la sección acerca de los objetos temporales para la // explicación de por qué esto funciona. v.swap(vector<Foo>()); diff --git a/es-es/elixir-es.html.markdown b/es-es/elixir-es.html.markdown new file mode 100644 index 00000000..885165a6 --- /dev/null +++ b/es-es/elixir-es.html.markdown @@ -0,0 +1,457 @@ +--- +language: elixir +contributors: + - ["Joao Marques", "http://github.com/mrshankly"] + - ["Dzianis Dashkevich", "https://github.com/dskecse"] + - ["Ryan Plant", "https://github.com/ryanplant-au"] +translator: + - ["Adrian Carrascal", "https://github.com/acarrascalgarcia"] +filename: learnelixir-es.ex +lang: es-es + +--- + +Elixir es un lenguaje funcional moderno construido sobre la máquina virtual de Erlang. +Es completamente compatibe con Erlang, sin embargo, ofrece una sintaxis más estandar +y otras características más. + +```elixir + +# Los comentarios de única línea +# comienzan con un símbolo numérico. + +# No hay comentarios multilinea, +# pero se pueden apilar varios comentarios. + +# Para usar el shell de elixir se usa el comando `iex`. +# Los módulos se compilan con el comando `elixirc`. + +# Ambos deberían estar en la ruta si elixir se instaló correctamente. + +## --------------------------- +## -- Tipos básicos +## --------------------------- + +# Hay números +3 # integer +0x1F # integer +3.0 # float + +# Átomos, que son literales, una constante con nombre. Comienzan con `:`. +:hello # atom + +# Tuples that are stored contiguously in memory. +# Tuplas que se almacenan contiguamente en memoria. +{1,2,3} # tuple + +# Se puede acceder a un elemento de una tupla con la función `elem`: +elem({1, 2, 3}, 0) #=> 1 + +# Listas que se implementan como listas enlazadas. +[1,2,3] # list + +# Se puede acceder al primer y último elemento de la lista como: +[head | tail] = [1,2,3] +head #=> 1 +tail #=> [2,3] + +# En elixir, solo como Erlang, el `=` denota la coincidencia de patrones y +# no una asignación. +# +# This is how the above example of accessing the head and tail of a list works. +# Así es como el ejemplo anterior de acceder al +# primer y último elemento de una lista trabaja. + +# Una coincidencia de patrón errará cuando los lados no coincidan, en este ejemplo +# las tuplas tienen diferentes tamaños. +# {a, b, c} = {1, 2} #=> ** (MatchError) no match of right hand side value: {1,2} + +# También hay binarios +<<1,2,3>> # binary + +# Cadenas y listas de caracteres +"hello" # string +'hello' # char list + +# Cadenas de varias lineas +""" +I'm a multi-line +string. +""" +#=> "I'm a multi-line\nstring.\n" + +# Todas las cadenas se codifican en UTF-8: +"héllò" #=> "héllò" + +# Las cadenas son solo binarios realmente, y la lista de caracteres solo listas. +<<?a, ?b, ?c>> #=> "abc" +[?a, ?b, ?c] #=> 'abc' + +# `?a` en elixir devuelve el valor ASCII para el caracter `a` +?a #=> 97 + +# Para concatenar listas se usa `++`, para binarios `<>` +[1,2,3] ++ [4,5] #=> [1,2,3,4,5] +'hello ' ++ 'world' #=> 'hello world' + +<<1,2,3>> <> <<4,5>> #=> <<1,2,3,4,5>> +"hello " <> "world" #=> "hello world" + +# Los rangos se representan como `start..end` (Es inclusivo) +1..10 #=> 1..10 +lower..upper = 1..10 # Se puede usar la coincidencia de patrones en los rangos también +[lower, upper] #=> [1, 10] + +# Los mapas son pares de llave-valor +genders = %{"david" => "male", "gillian" => "female"} +genders["david"] #=> "male" + +# Los mapas con llaves de tipo átomo se pueden usar como esto +genders = %{david: "male", gillian: "female"} +genders.gillian #=> "female" + +## --------------------------- +## -- Opetadores +## --------------------------- + +# Aritméticos +1 + 1 #=> 2 +10 - 5 #=> 5 +5 * 2 #=> 10 +10 / 2 #=> 5.0 + +# En elixir el operador `/` siempre devuelve un número flotante + +# Para hacer la división de número entero se debe usar `div` +div(10, 2) #=> 5 + +# Para obtener el residuo de la división se debe usar `rem` +rem(10, 3) #=> 1 + +# También hay operadores lógicos: `or`, `and` y `not`. +# Estos operadores esperan un boolean como su primer argumento. +true and true #=> true +false or true #=> true +# 1 and true #=> ** (ArgumentError) argument error + +# Elixir también provee `||`, `&&` y `!` donde acepta argumentos de cualquier tipo. +# Todos los valores excepto `false` y `nil` se evaluarán como verdadero. +1 || true #=> 1 +false && 1 #=> false +nil && 20 #=> nil +!true #=> false + +# Para comparaciones se tiene: `==`, `!=`, `===`, `!==`, `<=`, `>=`, `<` y `>` +1 == 1 #=> true +1 != 1 #=> false +1 < 2 #=> true + +# `===` y `!==` son más estrictos cuando comparan números: +1 == 1.0 #=> true +1 === 1.0 #=> false + +# También se puede comparar dos tipos de datos diferentes: +1 < :hello #=> true + +# No se necesita memorizar el orden pero es importante tenerlo en cuenta: +# number < atom < reference < functions < port < pid < tuple < list < bit string + +## --------------------------- +## -- Control de flujo +## --------------------------- + +# Expresión `if` +if false do + "This will never be seen" +else + "This will" +end + +# También está la expresión `unless` +unless true do + "This will never be seen" +else + "This will" +end + +# Se acuerda de la coincidencia de patrones? +# Muchas estructuras de control de flujo en elixir confían en ella. + +# `case` permite comparar un valor con muchos patrones: +case {:one, :two} do + {:four, :five} -> + "This won't match" + {:one, x} -> + "This will match and bind `x` to `:two` in this clause" + _ -> + "This will match any value" +end + +# Es común vincular el valor a `_` si no se necesita. +# Por ejemplo, si unicamente el primer elemento de la lista es importante: +[head | _] = [1,2,3] +head #=> 1 + +# Para una mejor lectura se puede hace lo siguiente: +[head | _tail] = [:a, :b, :c] +head #=> :a + +# `cond` permite comprobar muchas condiciones al mismo tiempo. +# Usar `cond` en vez de muchas expresiones `if` anidadas. +cond do + 1 + 1 == 3 -> + "I will never be seen" + 2 * 5 == 12 -> + "Me neither" + 1 + 2 == 3 -> + "But I will" +end + +# Es común estabecer la última condición como `true`, donde siempre va a coincidir. +cond do + 1 + 1 == 3 -> + "I will never be seen" + 2 * 5 == 12 -> + "Me neither" + true -> + "But I will (this is essentially an else)" +end + +# `try/catch` se usa para atrapar valores que se lanzan, también soporta una +# clausula `after` que se invoca sin importar si un valor se atrapó o no. +try do + throw(:hello) +catch + message -> "Got #{message}." +after + IO.puts("I'm the after clause.") +end +#=> I'm the after clause +# "Got :hello" + +## --------------------------- +## -- Módulos y Funciones +## --------------------------- + +# Anonymous functions (notice the dot) +# Funciones anónimas (Ver el punto `.`) +square = fn(x) -> x * x end +square.(5) #=> 25 + +# También aceptan muchas cláusulas y guards. +# Los guards permiten afinar las coincidencias de patrones, +# se indican por la palabra reservada `when`: +f = fn + x, y when x > 0 -> x + y + x, y -> x * y +end + +f.(1, 3) #=> 4 +f.(-1, 3) #=> -3 + +# Elixir también provee muchas funciones incorporadas. +# Esas están disponibles en el ámbito actual. +is_number(10) #=> true +is_list("hello") #=> false +elem({1,2,3}, 0) #=> 1 + +# Se pueden agrupar varias funciones en un módulo. Dentro de un módulo +# se usa `def` para definir las funciones. +defmodule Math do + def sum(a, b) do + a + b + end + + def square(x) do + x * x + end +end + +Math.sum(1, 2) #=> 3 +Math.square(3) #=> 9 + +# Para compilar el módulo simple de Math se guarda como `math.ex` y se usa `elixirc` +# en la terminal: elixirc math.ex + +# Dentro de un módulo se puede definir funciones con `def` y funciones privadas con `defp`. +# Una función definida con `def` está disponible para ser invocada desde otros módulos, +# una función privada se puede solo invocar localmente. +defmodule PrivateMath do + def sum(a, b) do + do_sum(a, b) + end + + defp do_sum(a, b) do + a + b + end +end + +PrivateMath.sum(1, 2) #=> 3 +# PrivateMath.do_sum(1, 2) #=> ** (UndefinedFunctionError) + +# La declaración de funciones también soportan guards y múltiples cláusulas: +defmodule Geometry do + def area({:rectangle, w, h}) do + w * h + end + + def area({:circle, r}) when is_number(r) do + 3.14 * r * r + end +end + +Geometry.area({:rectangle, 2, 3}) #=> 6 +Geometry.area({:circle, 3}) #=> 28.25999999999999801048 +# Geometry.area({:circle, "not_a_number"}) +#=> ** (FunctionClauseError) no function clause matching in Geometry.area/1 + +# Debido a la inmutabilidad, la recursión es una gran parte de elixir +defmodule Recursion do + def sum_list([head | tail], acc) do + sum_list(tail, acc + head) + end + + def sum_list([], acc) do + acc + end +end + +Recursion.sum_list([1,2,3], 0) #=> 6 + +# Los módulos de Elixir soportan atributos, hay atributos incorporados y +# se pueden agregar otros personalizados. +defmodule MyMod do + @moduledoc """ + This is a built-in attribute on a example module. + """ + + @my_data 100 # This is a custom attribute. + IO.inspect(@my_data) #=> 100 +end + +# El operador pipe |> permite que se pase la salida de una expresión +# como el primer parámetro en una función. + +Range.new(1,10) +|> Enum.map(fn x -> x * x end) +|> Enum.filter(fn x -> rem(x, 2) == 0 end) +#=> [4, 16, 36, 64, 100] + +## --------------------------- +## -- Structs and Excepciones +## --------------------------- + +# Los Structs son extensiones de los mapas que traen valores por defecto, +# garantes en tiempo de compilación y polimorfismo en Elixir. +defmodule Person do + defstruct name: nil, age: 0, height: 0 +end + +joe_info = %Person{ name: "Joe", age: 30, height: 180 } +#=> %Person{age: 30, height: 180, name: "Joe"} + +# Acceder al valor de name +joe_info.name #=> "Joe" + +# Actualizar el valor de age +older_joe_info = %{ joe_info | age: 31 } +#=> %Person{age: 31, height: 180, name: "Joe"} + +# El bloque `try` con la palabra reservada `rescue` se usa para manejar excepciones +try do + raise "some error" +rescue + RuntimeError -> "rescued a runtime error" + _error -> "this will rescue any error" +end +#=> "rescued a runtime error" + +# Todas las excepciones tienen un mensaje +try do + raise "some error" +rescue + x in [RuntimeError] -> + x.message +end +#=> "some error" + +## --------------------------- +## -- Concurrencia +## --------------------------- + +# Elixir confía en el modelo actor para la concurrencia. Todo lo que se necesita para escribir +# programas concurrentes en elixir son tres primitivas: procesos de desove, +# envío de mensajes y recepción de mensajes. + +# Para empezar un nuevo proceso se usa la función `spawn`, +# donde toma una función como argumento. +f = fn -> 2 * 2 end #=> #Function<erl_eval.20.80484245> +spawn(f) #=> #PID<0.40.0> + +# `spawn` devuelve un pid (identificador de proceso), se puede usar este pid para enviar +# mensajes para el proceso. Para hacer que un mensaje pase se usa el operador `send`. +# Para que todo esto se útil se necesita estar disponibles para recibir mensajes. Esto se +# alcanza con el mecanismo `receive`: + +# El bloque `receive do` se usa para escuchar los mensajes y procesarlos +# cuando se reciben. Un bloque `receive do` solo procesará +# un mensaje recibido. Para procesar múltiples mensajes, +# una función con un bloque `receive do` tiene que llamarse recursivamente +# para entrar en el bloque `receive do` otra vez. + +defmodule Geometry do + def area_loop do + receive do + {:rectangle, w, h} -> + IO.puts("Area = #{w * h}") + area_loop() + {:circle, r} -> + IO.puts("Area = #{3.14 * r * r}") + area_loop() + end + end +end + +# Compilar el módulo y crear un proceso que evalue `area_loop` en el shell +pid = spawn(fn -> Geometry.area_loop() end) #=> #PID<0.40.0> +# Como alternativa +pid = spawn(Geometry, :area_loop, []) + +# Enviar un mensaje al `pid` que coincidirá con un patrón en el que recibe una sentencia +send pid, {:rectangle, 2, 3} +#=> Area = 6 +# {:rectangle,2,3} + +send pid, {:circle, 2} +#=> Area = 12.56000000000000049738 +# {:circle,2} + +# El shell también es un proceso, se puede usar `self` para obtener el pid actual +self() #=> #PID<0.27.0> + +## --------------------------- +## -- Agentes +## --------------------------- + +# Un agente es un proceso que mantiene el seguimiento de algún valor cambiante + +# Un agente se crea con `Agent.start_link`, introducuendole una función +# El estado inicial del agente será lo que sea que la función devuelva +{ok, my_agent} = Agent.start_link(fn -> ["red, green"] end) + +# `Agent.get` toma un nombre de agente y un `fn` que se pasa como el estado actual +# Lo que sea que este `fn` devuelva es lo que se obtendrá de vuelta +Agent.get(my_agent, fn colors -> colors end) #=> ["red, "green"] + +# El estado del agente se actualiza de la misma manera +Agent.update(my_agent, fn colors -> ["blue" | colors] end) +``` + +## Referencias + +* [Getting started guide](http://elixir-lang.org/getting-started/introduction.html) from the [Elixir website](http://elixir-lang.org) +* [Elixir Documentation](http://elixir-lang.org/docs/master/) +* ["Programming Elixir"](https://pragprog.com/book/elixir/programming-elixir) by Dave Thomas +* [Elixir Cheat Sheet](http://media.pragprog.com/titles/elixir/ElixirCheat.pdf) +* ["Learn You Some Erlang for Great Good!"](http://learnyousomeerlang.com/) by Fred Hebert +* ["Programming Erlang: Software for a Concurrent World"](https://pragprog.com/book/jaerlang2/programming-erlang) by Joe Armstrong |