Impecable

Impecable

Ejercicio de aprendizaje

Introducción

¡Este es un tema enorme! Es posible escribir un libro largo al respecto, y varias personas lo han hecho (busca «unicode book» en Amazon para ver algunos ejemplos).

Una historia muy breve

Manejar caracteres en las computadoras era mucho más simple en décadas anteriores, cuando quienes programaban asumían que el inglés era el único idioma importante. Entonces: 26 letras, mayúsculas y minúsculas, 10 dígitos, varios signos de puntuación, más un código (0x07) para hacer sonar una campana, y todo cabía en 7 bits: el conjunto de caracteres ASCII.

Naturalmente, la gente empezó a preguntar qué pasaba con à, ä y Ł, luego otras personas empezaron a preguntar por ऄ, ஹ y ญ, y las personas jóvenes querían emojis 😱. ¿Qué hacer?

Para resumir una larga historia, muchas personas inteligentes y pacientes tuvieron que participar en comités durante años, definiendo los detalles del conjunto de caracteres Unicode y de codificaciones como UTF-8, y mucho software necesitó una reescritura muy complicada. Además, se introdujeron muchos bugs nuevos.

Para evitar que todo se rompiera, el diseño de Unicode/UTF-8 garantiza que los primeros 127 códigos son idénticos a ASCII (incluso la campana).

Los caracteres en Julia

Los lenguajes diseñados después de 2005 aproximadamente tienen la enorme ventaja de que ya existía un estándar Unicode razonablemente estable.

Julia (publicado por primera vez en 2012) pudo asumir que todo sería Unicode: caracteres, strings, nombres de variables y de funciones, operadores matemáticos...

Para citar el manual: «Julia hace que tratar con texto ASCII simple sea simple y eficiente, y manejar Unicode es tan simple y eficiente como sea posible». Fíjate en el «como sea posible»: es una parte importante de la afirmación.

Los literales de carácter se escriben entre comillas simples y son distintos de los strings escritos entre comillas dobles.

Esto es obvio para quienes vienen del mundo de C/C++, pero puede confundir a quienes programan en Python y JavaScript.

julia> a = 'a'  # Roman alphabet
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)

julia> typeof(a)
Char

julia> jha = 'झ'  # Devanagari alphabet
'झ': Unicode U+091D (category Lo: Letter, other)

julia> typeof(jha)
Char

julia> '❤'  # heart emoji
'❤': Unicode U+2764 (category So: Symbol, other)

En los ejemplos podemos ver que el tipo es Char, y que Julia tiene más información sobre la categoría del carácter.

Si miramos de cerca, parece que estos caracteres se pueden representar con 4 dígitos hexadecimales. El conjunto completo de caracteres necesita hasta 6 dígitos hexadecimales.

Estos números se llaman «puntos de código» y actualmente van desde U+0000 hasta U+10FFFF. Se muestran en el REPL, pero dentro del código usa codepoints() para obtenerlos.

Convertir entre Char e Int es simple:

julia> Int('a')
97

julia> Char(97)
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)

El compilador permite algunas formas de aritmética de enteros con Char:

julia> 'b' - 'a'  # interval, in alphabetic order
1

julia> 'b' + 'a'
ERROR: MethodError: no method matching +(::Char, ::Char)

julia> 'a' + 5
'f': ASCII/Unicode U+0066 (category Ll: Letter, lowercase)

julia> 'f' + ('A' - 'a')  # same as `uppercase('f')`
'F': ASCII/Unicode U+0046 (category Lu: Letter, uppercase)

Funciones para caracteres

Muchas funciones para manejar strings también funcionan cuando reciben un Char como argumento.

  • Para los alfabetos correspondientes, cambia el caso con uppercase() y lowercase().
  • Comprueba el caso con isuppercase() e islowercase().
  • Comprueba el tipo de carácter con:
    • isletter(), abarca muchos alfabetos
    • isdigit(), comprueba estrictamente 0:9
    • isnumeric(), más amplia que isdigit, así que da true para ¾ y varios sistemas de escritura no europeos
    • isxdigit(), dígitos hexadecimales
    • isascii(), carácter anterior a Unicode
    • ispunct(), puntuación
    • isspace(), cualquier carácter de espacio en blanco
    • isprint(), caracteres imprimibles (lo contrario es iscntrl())
islowercase('A')  # false
uppercase('γ')  # 'Γ': Unicode U+0393 (category Lu: Letter, uppercase)
ispunct('@')  # true
isdigit('A')  # false
isxdigit('A')  # true

Conversiones entre vectores de Char y String

Para pasar de un String a un vector de Char, podemos usar collect().

Para pasar de un vector de Char a un String, está el constructor String().

julia> s = "abcde"
"abcde"

julia> cv = collect(s)
5-element Vector{Char}:
 'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)
 'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase)
 'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase)
 'd': ASCII/Unicode U+0064 (category Ll: Letter, lowercase)
 'e': ASCII/Unicode U+0065 (category Ll: Letter, lowercase)

julia> String(cv)
"abcde"

Esto funciona con cualquier carácter, no solo con ASCII.

julia> collect("❤,😱")
3-element Vector{Char}:
 '❤': Unicode U+2764 (category So: Symbol, other)
 ',': ASCII/Unicode U+002C (category Po: Punctuation, other)
 '😱': Unicode U+1F631 (category So: Symbol, other)

Fíjate en que el constructor String() trabaja sobre un Vector. Para convertir un solo Char a un string de 1 carácter, la función es string(), con la s minúscula.

julia> string('a')
"a"

Almacenamiento

Todo lo que hemos visto hasta ahora en este documento parece relativamente simple, así que ¿de verdad no hay mucho de qué preocuparse?

Lamentablemente, ¡esto es demasiado optimista!

Una complicación viene de la necesidad de hasta 6 dígitos hexadecimales por punto de código. Esto significa que distintos caracteres necesitan distintas cantidades de espacio en memoria cuando se codifican en UTF-8.

Un byte solo puede almacenar números (sin signo) hasta 255, dos dígitos hexadecimales, así que UTF-8 usa una cantidad variable de bytes (de 1 a 4) para almacenar un Char. Estos se llaman «unidades de código», y la función ncodeunits() devuelve la cantidad necesaria para un carácter dado.

julia> codepoint(jha)  # jha 'झ' is defined in an earlier example
0x0000091d

julia> ncodeunits(jha)
3

julia> ncodeunits('a')  # ASCII character
1

julia> ncodeunits('😱')  # emoji
4

Además, no todo lo que se puede mostrar en pantalla tiene su propio punto de código único. Algunos caracteres visualmente distintos se consideran derivados de otros, así que Unicode los trata como un carácter base más un modificador.

Este problema afecta a los strings, donde presenta dificultades para la indexación.

Instrucciones

En este ejercicio implementarás un conjunto parcial de rutinas de utilidad para ayudar a un desarrollador a limpiar nombres de identificadores.

A lo largo de las 6 tareas irás construyendo poco a poco las funciones transform, para convertir caracteres individuales, y clean, para convertir strings.

Un identificador válido se compone de cero o más letras, guiones bajos, guiones, signos de interrogación y emojis.

Si se pasa un string vacío a la función clean, se debe devolver un string vacío.

1. Reemplaza los guiones que encuentres por guiones bajos

Implementa la función transform para reemplazar los guiones por guiones bajos.

julia> transform('-')
"_"

2. Elimina todos los espacios en blanco

Elimina todos los caracteres de espacio en blanco. Esto incluye los espacios en blanco al principio y al final.

julia> transform(' ')
""

3. Convierte camelCase a kebab-case

Modifica la función transform para convertir camelCase a kebab-case

julia> transform('D')
"-d"

4. Omite los caracteres que sean dígitos

Modifica la función transform para omitir los caracteres que sean numéricos.

julia> transform('7')
""

5. Reemplaza las letras griegas minúsculas por signos de interrogación

Modifica la función transform para reemplazar las letras griegas en el rango de 'α' a 'ω'.

julia> transform('β')
"?"

6. Combina estas operaciones para trabajar sobre un string

Implementa la función clean para aplicar estas operaciones a un string completo.

Los caracteres que queden fuera de las reglas deben pasar sin cambios.

julia> clean("  a2b Cd-ω😀  ")
"ab-cd_?😀"
Editar en GitHub El enlace se abre en una ventana o una pestaña nuevas
Julia Exercism

¿Todo listo para empezar Impecable?

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