Tracks
/
Julia
Julia
/
Ejercicios
/
Amor de secundaria
Amor de secundaria

Amor de secundaria

Ejercicio de aprendizaje

Introducción

Julia anima a quienes programan a poner la mayor cantidad de código 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 simples, 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 el punto.

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 hace más profundo.

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

Composición

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

Un número arbitrario de funciones se pueden composed entre sí con operadores ∘ (se escribe \circ y luego tab). El resultado se puede usar como una sola 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 notar:

  • Las funciones iniciales 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.

Canalización

Una alternativa podría considerarse el enfoque de los programadores, en lugar del de los matemáticos.

Pipelines se han usado durante mucho tiempo en los scripts de shell de Unix y, más recientemente, se volvieron populares en los lenguajes de programación más usados (a veces se atribuye a F# haber sido pionero en su adopción).

El concepto básico es empezar con algunos datos y luego hacerlos pasar por una secuencia de funciones para obtener el resultado.

El operador de tubería es |> (como en F# y las 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)

Ahora la ejecución es estrictamente de izquierda a derecha, y la salida de cada función fluye en la dirección de la flecha para convertirse en el argumento de la siguiente función.

Limitaciones, soluciones alternativas y otras opciones

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

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

En cambio, Julia solo espera nombres de funciones (o algo equivalente) en una canalización, sin argumentos adicionales.

Hay razones técnicas importantes para esto (relacionadas con el hecho de que currying no forma parte estándar del diseño del lenguaje). Las muchas personas que no entienden el currying solo deberían 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 simple 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 luego canalizarse hacia otras funciones, como antes.

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

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

Instrucciones

En este ejercicio, vas a ayudar a unos novios de secundaria 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, quitar todos los caracteres - y reemplazarlos por un espacio. También debe quitar 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. Formatea la primera letra como una 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 rodeadas de corazones emoji. 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 una pestaña nuevas
Julia Exercism

¿Todo listo para empezar Amor de secundaria?

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