Track
/
Julia
Julia
/
Esercizi
/
Livelli di log
Livelli di log

Livelli di log

Esercizio di apprendimento

Introduzione

In Julia, una String è una sequenza ordinata di caratteri. Come fa notare il manuale: «Certo, il vero problema nasce quando ci si chiede che cosa sia un carattere».

Gran parte di questa complessità sarà trattata più in dettaglio nel concetto Chars, ma in breve:

  • In Julia, tutte le stringhe sono Unicode.
  • Le stringhe che sono anche ASCII (le lettere inglesi dalla A alla Z in maiuscolo e minuscolo, le cifre e qualche carattere di punteggiatura) sono facili da usare.
  • Con la maggior parte degli altri sistemi di scrittura del mondo è possibile lavorare, ma serve più attenzione.

Stringhe letterali

In generale, le stringhe sono racchiuse tra virgolette doppie " ". Le virgolette singole ' ' si usano solo per Chars.

È anche possibile usare triple virgolette doppie """ """, il che permette di usare " all'interno della stringa senza escape. Più importante ancora, l'indentazione indesiderata in eccesso viene rimossa dalle stringhe multilinea.

julia> """
       Multiline
       
       """
"Multiline\nString\n"

Caratteri di escape

Come la maggior parte dei linguaggi a partire dal C, Julia usa la barra rovesciata \ per l'escape:

  1. Per inserire caratteri non stampabili, come il carattere di nuova riga \n nell'esempio precedente.
  2. Per proteggere caratteri che altrimenti avrebbero un significato speciale, come \$ (vedi sotto).

Indicizzazione

In linea di principio, le stringhe si possono indicizzare esattamente come i vettori:

julia> s[1], s[5]
('J', 'a')

Funziona chiaramente per una stringa ASCII come "Julia", ma così si ignorano la maggior parte delle lingue del mondo.

Consideriamo uno dei caratteri del Beowulf, scritto in inglese antico circa 15 secoli fa.

"Hrōðgār" è chiaramente formato da 7 caratteri, ma non tutti sono ASCII.

julia> king = "Hrōðgār"
"Hrōðgār"

julia> length(king)
7

julia> isascii(king)
false

# collect() converts to a Char vector:
julia> collect(king)
7-element Vector{Char}:
 'H': ASCII/Unicode U+0048 (category Lu: Letter, uppercase)
 'r': ASCII/Unicode U+0072 (category Ll: Letter, lowercase)
 'ō': Unicode U+014D (category Ll: Letter, lowercase)
 'ð': Unicode U+00F0 (category Ll: Letter, lowercase)
 'g': ASCII/Unicode U+0067 (category Ll: Letter, lowercase)
 'ā': Unicode U+0101 (category Ll: Letter, lowercase)
 'r': ASCII/Unicode U+0072 (category Ll: Letter, lowercase)

L'indicizzazione di una stringa come questa parte bene, ma a un certo punto si rompe:

julia> king[3]
'ō': Unicode U+014D (category Ll: Letter, lowercase)

julia> king[4]
ERROR: StringIndexError: invalid index [4], valid nearby indices [3]=>'ō', [5]=>'ð'

Il problema è che un'indicizzazione di questo tipo conta i byte, che per le stringhe non ASCII non hanno una relazione 1:1 con i caratteri. Un carattere Unicode/UTF-8 può richiedere fino a 4 byte per essere rappresentato, e il concetto Chars lo approfondirà.

Costruire le stringhe

Concatenazione

Molti linguaggi usano + come operatore di concatenazione delle stringhe, ma non Julia. Almeno il messaggio di errore è utile.

julia> "s1 " + "s2"
ERROR: MethodError: no method matching +(::String, ::String)
The function `+` exists, but no method is defined for this combination of argument types.
String concatenation is performed with * 

julia> "s1 " * "s2"
"s1 s2"

La funzione string() concatena un numero arbitrario di stringhe.

julia> string("lots ", "of ", "bits")
"lots of bits"

La funzione join() fa lo stesso e accetta anche un vettore di stringhe, concatenandole con un separatore dato (che per impostazione predefinita è "").

julia> join(["lots", "of", "bits"], "_")
"lots_of_bits"

Nota che le stringhe sono immutabili e che la concatenazione comporta una copia. Quindi, concatenazioni ripetute all'interno di un ciclo sono inefficienti: conviene raccogliere i frammenti in un vettore e unirli tutti alla fine.

Ripetizione

Semplice:

julia> repeat("Abc", 5)
"AbcAbcAbcAbcAbc"

Interpolazione

Molti linguaggi hanno un modo per inserire valori calcolati dentro le stringhe, anche se c'è un disaccordo straordinario sulla sintassi migliore da usare.

In Julia, i valori interpolati sono preceduti da $.

julia> myvar = 42
42

julia> "myvar evaluates to $myvar"
"myvar evaluates to 42"

julia> r = 5.3
5.3

julia> "A circle of radius $r has area $(π * r^2)"
"A circle of radius 5.3 has area 88.24733763933729"

Le parentesi sono obbligatorie quando ometterle creerebbe ambiguità, ma facoltative per un singolo identificatore seguito da uno spazio bianco.

Per includere un $ nella stringa, fai l'escape con \$.

Nota il limite per cui l'interpolazione al momento non consente la formattazione, come il numero di cifre decimali da mostrare.

@sprintf()

Per un controllo più fine sull'assemblaggio delle stringhe, Julia riprende la funzione sprintf dal C (e da diversi linguaggi successivi: Wikipedia ne elenca circa 30).

In Julia, è implementata come macro all'interno del modulo Printf, da cui il prefisso @.

julia> using Printf

julia> r = 5.3
5.3

julia> @sprintf("A circle of radius %.1f has area %.2f", r, π * r^2)
"A circle of radius 5.3 has area 88.25"

Funzioni per le stringhe

Per semplicità, questa sezione si concentrerà sulle funzioni che restituiscono una nuova stringa e lasciano invariato l'input. Molte hanno un equivalente, indicato con il suffisso !, che modifica l'input sul posto.

Lunghezza

Quanto è lunga una stringa? A volte dipende da come la si conta.

Il caso semplice è quello delle stringhe ASCII, in cui ogni carattere occupa 1 byte.

julia> string_asc = "Julia"
"Julia"

julia> length(string_asc)
5

julia> sizeof(string_asc)
5

La funzione length() restituisce il numero di caratteri, mentre la funzione sizeof() il numero di byte.

Questa distinzione diventa importante con altri insiemi di caratteri:

julia> king = "Hrōðgār"
"Hrōðgār"

julia> length(king)
7

julia> sizeof(king)
10

Suddivisione

Un'operazione comune in programmazione consiste nel prendere una stringa lunga e suddividerla in pezzi, in base a una stringa separatrice di uno o più caratteri.

Più in generale, si usa la funzione split(). Il separatore predefinito è uno spazio bianco (uno qualsiasi), ma se ne possono specificare altri.

julia> split("my little string")
3-element Vector{SubString{String}}:
 "my"
 "little"
 "string"

julia> split("My_snake_case_string", "_")
4-element Vector{SubString{String}}:
 "My"
 "snake"
 "case"
 "string"

Questi esempi suddividono una stringa in stringhe più piccole. Suddividere una stringa in caratteri è un'operazione diversa.

Trovare e sostituire

Una stringa contiene una sottostringa specificata? Stranamente, ci sono due funzioni per verificarlo: occursin() e contains() differiscono solo per l'ordine degli argomenti.

julia> needle = "Julia"
"Julia"

julia> haystack = "Python, Julia and R"
"Python, Julia and R"

julia> occursin(needle, haystack)
true

julia> contains(haystack, needle)
true

Per essere più precisi sulla posizione:

julia> startswith(haystack, "Python")
true

julia> endswith(haystack, "Python")
false

Per ottenere gli indici inizio:fine di una sottostringa, abbiamo diverse funzioni:

  • findfirst()
  • findlast()
  • findprev()
  • findnext()
julia> findfirst(needle, haystack)
9:13

Per sostituire le sottostringhe, elenca le modifiche usando la sintassi x => y.

julia> replace("abc", "a"=>"b", "b"=>"c", "c"=>"a")
"bca"

Dalla versione 1.7 di Julia, la funzione replace accetta un numero arbitrario di modifiche, con la garanzia che nessun carattere venga modificato più di una volta durante la sostituzione.

La sintassi x => y si chiama pair, un tipo importante in Julia, trattato più in dettaglio nel concetto Dicts and Pairs.

Spazi bianchi

Spesso dobbiamo rimuovere gli spazi bianchi iniziali e/o finali da una stringa.

Le funzioni per farlo sono lstrip() (a sinistra), rstrip() (a destra), strip() (entrambi).

julia> strip("\n\t  useful_bit\n\n")
"useful_bit"

Cambiare maiuscole e minuscole

Queste funzioni hanno nomi piuttosto espliciti:

julia> s = repeat("aBcD ", 5)
"aBcD aBcD aBcD aBcD aBcD "

julia> uppercase(s)
"ABCD ABCD ABCD ABCD ABCD "

julia> lowercase(s)
"abcd abcd abcd abcd abcd "

julia> titlecase(s)  # Initial letter of each word uppercase
"Abcd Abcd Abcd Abcd Abcd "

Istruzioni

In questo esercizio elaborerai delle righe di log.

Ogni riga di log è una stringa formattata così: "[<LEVEL>]: <MESSAGE>".

Ci sono tre livelli di log diversi:

  • INFO
  • WARNING
  • ERROR

Hai tre compiti, ognuno dei quali riceve una riga di log e ti chiede di fare qualcosa con essa.

1. Ottenere il messaggio da una riga di log

Implementa la funzione message in modo che restituisca il messaggio di una riga di log:

julia> message("[ERROR]: Invalid operation")
"Invalid operation"

Gli spazi bianchi all'inizio e alla fine vanno rimossi:

julia> message("[WARNING]:  Disk almost full\r\n")
"Disk almost full"

2. Ottenere il livello di log da una riga di log

Implementa la funzione log_level in modo che restituisca il livello di log di una riga di log, che deve essere restituito in minuscolo:

julia> log_level("[ERROR]: Invalid operation")
"error"

3. Riformattare una riga di log

Implementa la funzione reformat che riformatta la riga di log, mettendo prima il messaggio e poi il livello di log tra parentesi:

julia> reformat("[INFO]: Operation completed")
"Operation completed (info)"

Nota: Tutte le stringhe in questo esercizio sono in inglese e limitate al set di caratteri ASCII. Nei concetti successivi avrai modo di lavorare con i caratteri Unicode.

Modifica tramite GitHub Il link si apre in una nuova finestra o scheda
Julia Exercism

Vuoi iniziare Livelli di log?

Iscriviti a Exercism per imparare e padroneggiare Julia con 35 concetti128 esercizi e il mentoring di persone reali, tutto gratis.