Rutas
/
Julia
Julia
/
Ejercicios
/
Amor de instituto
Amor de instituto

Amor de instituto

Ejercicio de aprendizaje

Introducción

Julia anima a quienes programan a incluir tanto código como sea posible dentro de funciones que se puedan compilar JIT, y crear muchas funciones pequeñas es, por diseño, eficiente.

Eso tiende a dejar muchas funciones pequeñas y sencillas, que hay que combinar para llevar a cabo tareas no triviales.

Una opción obvia es anidar llamadas a funciones. El siguiente ejemplo es muy rebuscado, pero ilustra la idea.

julia> first.(titlecase.(reverse.(["my", "test", "strings"])))
3-element Vector{Char}:
 'Y': ASCII/Unicode U+0059 (category Lu: Letter, uppercase)
 'T': ASCII/Unicode U+0054 (category Lu: Letter, uppercase)
 'S': ASCII/Unicode U+0053 (category Lu: Letter, uppercase)

La desventaja de este enfoque es que la legibilidad cae rápidamente a medida que el anidamiento se vuelve más profundo.

Necesitamos un enfoque más sencillo y más flexible.

Composición

Esta es la técnica predilecta de los matemáticos, y Julia copia la sintaxis matemática.

Con composed se pueden combinar entre sí un número arbitrario de funciones mediante operadores ∘ (que se escriben como \circ y luego tabulador). El resultado se puede usar como una única función.

julia> compfunc = first ∘ titlecase ∘ reverse
first ∘ titlecase ∘ reverse
julia> compfunc.(["my", "test", "strings"])
3-element Vector{Char}:
 'Y': ASCII/Unicode U+0059 (category Lu: Letter, uppercase)
 'T': ASCII/Unicode U+0054 (category Lu: Letter, uppercase)
 'S': ASCII/Unicode U+0053 (category Lu: Letter, uppercase)
# alternative syntax, giving the same result
julia> (first ∘ titlecase ∘ reverse).(["my", "test", "strings"])

Un par de cosas que conviene tener en cuenta:

  • Las funciones de partida aparecen en el mismo orden que al anidar y se ejecutan de derecha a izquierda.
  • El broadcasting no es sencillo de usar al componer, pero se puede aplicar al llamar a la función compuesta.

Tuberías

Una alternativa podría considerarse el enfoque de los programadores más que el de los matemáticos.

Las Pipelines se usan desde hace mucho tiempo en los scripts de shell de Unix y, más recientemente, se han vuelto populares en los lenguajes de programación convencionales (a veces se atribuye a F# haber sido pionero en adoptarlas).

El concepto básico consiste en partir de unos datos y luego hacerlos pasar por una secuencia de funciones para obtener el resultado.

El operador de tubería es |> (como en F# y en versiones recientes de R), aunque Julia también tiene una versión con broadcasting, .|>.

julia> ["my", "test", "strings"] .|> reverse .|> titlecase .|> first
3-element Vector{Char}:
 'Y': ASCII/Unicode U+0059 (category Lu: Letter, uppercase)
 'T': ASCII/Unicode U+0054 (category Lu: Letter, uppercase)
 'S': ASCII/Unicode U+0053 (category Lu: Letter, uppercase)

La ejecución ahora es estrictamente de izquierda a derecha, y la salida de cada función fluye en la dirección de la flecha para convertirse en la entrada de la siguiente función.

Limitaciones, soluciones alternativas y otras opciones

No es casualidad que las funciones usadas para ilustrar la composición y las tuberías tomen un único argumento.

Algunos lenguajes puramente funcionales canalizan el primer argumento hacia una función, pero permiten incluir otros.

En cambio, Julia solo espera nombres de funciones (o algo equivalente) en una tubería, sin argumentos adicionales.

Hay razones técnicas importantes para ello (relacionadas con el hecho de que el currying no forma parte estándar del diseño del lenguaje). Las muchas personas que no tienen ni idea de currying deberían limitarse a aceptar que esta limitación no es un descuido, y que no es probable que cambie en futuras versiones de Julia.

Soluciones alternativas

Necesitamos funciones de un solo argumento que hagan lo que haga falta. Por suerte, definir funciones nuevas en Julia es fácil.

Lo más sencillo sería usar una anonymous function. Por ejemplo, si tenemos un único string de entrada y queremos dividirlo por guiones bajos:

julia> "my_test_strings" |> (s -> split(s, '_'))
3-element Vector{SubString{String}}:
 "my"
 "test"
 "strings"

Ese vector podría entonces pasarse por una tubería hacia otras funciones, como antes.

Encerrar la función anónima entre paréntesis es opcional en este caso, pero en general es una forma útil de reducir la ambigüedad.

Del mismo modo, podríamos crear una función con nombre antes en el programa y reutilizarla siempre que haga falta.

Instrucciones

En este ejercicio, vas a ayudar a unos novios de instituto a declarar su amor en las redes sociales generando un corazón unicode con sus iniciales:

❤ J.  +  M. ❤

1. Limpia el nombre

Implementa el método cleanupname. Debe recibir un nombre y eliminar todos los caracteres -, sustituyéndolos por un espacio. También debe eliminar los espacios en blanco del principio y del final del nombre.

julia> cleanupname("Jane-Ann")
"Jane Ann"

2. Obtén la primera letra del nombre

Implementa el método firstletter. Debe recibir un nombre y devolver su primera letra como string. Asegúrate de reutilizar cleanupname, que definiste en el paso anterior, y de combinarla con otras funciones.

julia> firstletter("Jane")
"J"

3. Da formato a la primera letra como inicial

Implementa el método initial. Debe recibir un nombre y devolver su primera letra en mayúscula, seguida de un punto. Asegúrate de reutilizar firstletter, que definiste en el paso anterior.

initial("Robert")
"R."

4. Coloca las iniciales dentro del corazón

Implementa el método couple. Debe recibir dos nombres y devolver las iniciales con corazones emoji alrededor. Asegúrate de reutilizar initial, que definiste en el paso anterior.

couple("Blake Miller", "Riley Lewis")
"❤ B.  +  R. ❤"
Editar en GitHub El enlace se abre en una ventana o pestaña nueva
Julia Exercism

¿Listo para empezar Amor de instituto?

Regístrate en Exercism para aprender y dominar Julia con 35 conceptos128 ejercicios y mentoría humana real, todo gratis.