Треки
/
Julia
Julia
/
Вправи
/
Рівні журналу
Рівні журналу

Рівні журналу

Навчальна вправа

Вступ

String у Julia - це впорядкована послідовність символів. Як зазначає посібник: «Звісно, справжні труднощі починаються тоді, коли постає питання, що таке символ».

Багато з цієї складності буде докладніше розглянуто в концепті Chars, але коротко:

  • Усі рядки тексту (англ. string) в Julia мають кодування Unicode.
  • Рядки тексту, які до того ж є ASCII (англійські літери від A до Z у верхньому й нижньому регістрі, цифри та кілька розділових знаків), з ними легко працювати.
  • З більшістю інших систем письма світу можна працювати, але вони потребують більшої уважності.

Рядкові літерали

Загалом рядки тексту беруться в подвійні лапки " ". Одинарні лапки ' ' використовуються лише для Chars.

Також можна використовувати потрійні подвійні лапки """ """, що дає змогу вживати " усередині рядка тексту без екранування. Що важливіше, у багаторядкових рядках тексту прибирається зайвий відступ.

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

Екранування символів

Як і більшість мов, починаючи з C, Julia використовує зворотну скісну риску \ для екранування:

  1. Щоб увести недруковані символи, як-от символ нового рядка \n у попередньому прикладі.
  2. Щоб захистити символи, які інакше мали б особливе значення, як-от \$ (див. нижче).

Індексування

У принципі, рядки тексту можна індексувати так само, як масиви:

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

Це чудово працює для ASCII-рядка, як-от "Julia", але так ми ігноруємо більшість мов світу.

Розгляньмо один із символів у «Беовульфі», написаному староанглійською близько 15 століть тому.

"Hrōðgār" явно складається з 7 символів, але не всі вони належать до 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)

Індексування такого рядка тексту спочатку працює добре, але в якийсь момент ламається:

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

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

Проблема в тому, що таке індексування рахує байти, а в не-ASCII рядках тексту вони не мають співвідношення 1:1 із символами. Символ Unicode/UTF-8 може потребувати до 4 байтів для свого подання, і концепт Chars розгляне це докладніше.

Побудова рядків тексту

Конкатенація

Багато мов використовують + як оператор конкатенації рядків тексту, але не Julia. Принаймні повідомлення про помилку тут корисне.

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"

Функція string() конкатенує довільну кількість рядків тексту.

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

Функція join() робить те саме, а також приймає масив рядків тексту й конкатенує їх із заданим роздільником (з типовим значенням "").

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

Зауважмо, що рядки тексту незмінні, а конкатенація передбачає копіювання. Тож багаторазова конкатенація всередині циклу є неефективною, і краще зібрати фрагменти в масив і зʼєднати їх усі наприкінці.

Повторення

Просто:

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

Інтерполяція

У багатьох мовах є спосіб вставляти обчислені значення в рядки тексту, хоча щодо найкращого синтаксису для цього існує надзвичайна розбіжність думок.

У Julia перед інтерпольованими значеннями ставиться $.

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"

Дужки обовʼязкові, коли їх пропуск створив би неоднозначність, але необовʼязкові для одного ідентифікатора, за яким іде пробільний символ.

Щоб включити $ у рядок тексту, екрануймо його як \$.

Зауважмо обмеження: інтерполяція наразі не дозволяє форматування, як-от задання кількості десяткових знаків для виведення.

@sprintf()

Для тоншого контролю над збиранням рядків тексту Julia запозичує функцію sprintf із C (і кількох пізніших мов: Вікіпедія наводить близько 30).

У Julia це реалізовано як macro у модулі Printf, звідси префікс @.

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"

Функції для рядків тексту

Для простоти цей розділ зосередиться на функціях, які повертають новий рядок тексту і залишають вхідні дані без змін. Багато з них мають відповідник із суфіксом !, який змінює вхідні дані на місці.

Довжина

Якої довжини рядок тексту? Іноді це залежить від того, як ми рахуємо.

У найпростішому випадку це ASCII-рядки, де кожен символ займає 1 байт.

julia> string_asc = "Julia"
"Julia"

julia> length(string_asc)
5

julia> sizeof(string_asc)
5

Функція length() повертає кількість символів, а функція sizeof() - кількість байтів.

Ця різниця стає важливою з іншими наборами символів:

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

julia> length(king)
7

julia> sizeof(king)
10

Розбиття

Поширена операція в програмуванні - узяти довгий рядок тексту й розбити його на частини за роздільником з одного або кількох символів.

Найзагальніше ми використовуємо функцію split(). Типовим роздільником є (будь-який) пробільний символ, але можна вказати й інші.

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"

У цих прикладах рядок тексту розбивається на менші рядки тексту. Розбиття рядка тексту на символи - це інша операція.

Пошук і заміна

Чи містить рядок тексту заданий підрядок? Як не дивно, для цього є дві функції: occursin() і contains() відрізняються лише порядком своїх аргументів.

julia> needle = "Julia"
"Julia"

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

julia> occursin(needle, haystack)
true

julia> contains(haystack, needle)
true

Щоб точніше вказати позицію:

julia> startswith(haystack, "Python")
true

julia> endswith(haystack, "Python")
false

Щоб отримати індекси початку й кінця підрядка, у нас є кілька функцій:

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

Щоб замінити підрядки, перелічімо зміни за допомогою синтаксису x => y.

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

Починаючи з Julia 1.7, функція replace приймає довільну кількість змін із гарантією, що жоден символ не буде змінено більше ніж один раз під час заміни.

Синтаксис x => y називається pair. Це важливий тип у Julia, який докладніше розглядається в концепті Dicts and Pairs.

Пробільні символи

Часто нам потрібно прибрати пробільні символи на початку та/або в кінці рядка тексту.

Для цього є функції lstrip() (зліва), rstrip() (справа), strip() (з обох боків).

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

Зміна регістру

Назви цих функцій доволі промовисті:

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 "

Вказівки

У цій вправі ми будемо обробляти рядки логу.

Кожен рядок логу записано як рядок тексту (англ. string) у такому форматі: "[<LEVEL>]: <MESSAGE>".

Є три різні рівні логу:

  • INFO
  • WARNING
  • ERROR

На нас чекають три завдання, кожне з яких отримає рядок логу й попросить нас щось із ним зробити.

1. Отримати повідомлення з рядка логу

Реалізуйте функцію message, щоб вона повертала повідомлення рядка логу:

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

Усі пробіли на початку й у кінці потрібно прибрати:

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

2. Отримати рівень логу з рядка логу

Реалізуйте функцію log_level, щоб вона повертала рівень логу з рядка логу, причому в нижньому регістрі:

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

3. Переформатувати рядок логу

Реалізуйте функцію reformat, яка переформатовує рядок логу: ставить повідомлення першим, а рівень логу після нього в дужках:

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

Примітка: Усі рядки тексту в цій вправі записані англійською й обмежені набором символів ASCII. Пізніші концепції дадуть змогу попрацювати з символами Unicode.

Редагувати через GitHub Посилання відкривається в новому вікні або вкладці
Julia Exercism

Час розпочати Рівні журналу?

Зареєструйтеся на Exercism, щоб вивчати й опановувати Julia, а також 35 концепцій128 вправ та справжнє наставництво від людей, і все це безкоштовно.