Треки
/
Julia
Julia
/
Вправи
/
Бездоганно чисто
Бездоганно чисто

Бездоганно чисто

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

Вступ

Це велика тема! Про неї можна написати цілу книжку, і кілька людей так і зробили (пошукайте на Amazon «unicode book», щоб побачити приклади).

Дуже коротка історія

Робота із символами в компʼютерах була значно простішою в попередні десятиліття, коли програмісти вважали, що англійська є єдиною важливою мовою. Отже: 26 літер у верхньому і нижньому регістрі, 10 цифр, кілька розділових знаків, плюс код (0x07), щоб продзвонити в дзвіночок, і все це вміщалося в 7 бітів: набір символів ASCII.

Звісно, люди почали питати, а як же à, ä та Ł, потім інші почали питати про ऄ, ஹ і ญ, а молодь захотіла емодзі 😱. Що робити?

Якщо коротко, багатьом розумним і терплячим людям довелося роками засідати в комітетах, опрацьовуючи деталі набору символів Unicode та кодувань на кшталт UTF-8, а багато програмного забезпечення потребувало дуже складної переробки. До того ж зʼявилося багато нових багів.

Щоб усе не зламалося, дизайн Unicode/UTF-8 гарантує, що перші 127 кодів збігаються з ASCII (навіть дзвіночок).

Символи в Julia

Мови, розроблені після приблизно 2005 року, мають величезну перевагу: досить стабільний стандарт Unicode уже існував.

Julia (уперше випущена 2012 року) могла припускати, що все буде Unicode: символи, рядки тексту (англ. string), імена змінних і функцій, математичні оператори...

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

Символьні літерали записуються в одинарних лапках і відрізняються від рядків тексту, записаних у подвійних лапках.

Це очевидно для людей зі світу C/C++, але може заплутати програмістів Python і 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)

З прикладів видно, що тип - Char, і Julia має додаткову інформацію про категорію символу.

Придивившись уважніше, бачимо, що ці символи можна подати 4 шістнадцятковими цифрами. А повний набір символів потребує до 6 шістнадцяткових цифр.

Ці числа називають «кодовими точками», і наразі вони охоплюють діапазон від U+0000 до U+10FFFF. Вони показуються в REPL, але в коді для їх отримання використовуйте codepoints().

Перетворити Char на Int і навпаки дуже просто:

julia> Int('a')
97

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

Компілятор дозволяє деякі форми цілочисельної арифметики над 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)

Функції для символів

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

  • Для відповідних алфавітів змінюємо регістр за допомогою uppercase() та lowercase().
  • Перевіряємо регістр за допомогою isuppercase() та islowercase().
  • Перевіряємо тип символу за допомогою:
    • isletter() охоплює багато алфавітів
    • isdigit() перевіряє строго 0:9
    • isnumeric() ширша за isdigit, тож дає true для ¾ і різних неєвропейських письменностей
    • isxdigit() для шістнадцяткових цифр
    • isascii() для символів до Unicode
    • ispunct() для розділових знаків
    • isspace() для будь-якого пробільного символу
    • isprint() для друкованих символів (протилежна до неї - iscntrl())
islowercase('A')  # false
uppercase('γ')  # 'Γ': Unicode U+0393 (category Lu: Letter, uppercase)
ispunct('@')  # true
isdigit('A')  # false
isxdigit('A')  # true

Взаємні перетворення вектора Char і рядка тексту

Щоб перетворити рядок тексту на вектор Char, можна скористатися collect().

А щоб перетворити вектор Char на рядок тексту, є конструктор 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"

Це працює з будь-якими символами, не лише з 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)

Зауважте, що конструктор String() працює з вектором. Щоб привести один Char до рядка тексту з одного символу, використовують функцію string(), з малої s.

julia> string('a')
"a"

Зберігання

Усе, про що йшлося в цьому документі досі, здається відносно простим, тож невже справді немає про що хвилюватися?

На жаль, це надто оптимістично!

Одна зі складнощів походить з потреби в «до» 6 шістнадцяткових цифрах на кодову точку. Це означає, що різним символам потрібен різний обсяг памʼяті за кодування UTF-8.

Байт може зберігати лише (беззнакові) числа до 255, тобто дві шістнадцяткові цифри, тож UTF-8 використовує змінну кількість байтів (від 1 до 4), щоб зберегти Char. Їх називають «кодовими одиницями», і функція ncodeunits() поверне кількість, потрібну для заданого символу.

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

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

Ця проблема стосується рядків тексту, де вона створює труднощі з індексацією.

Вказівки

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

У цих 6 завданнях ми поступово побудуємо функції transform для перетворення окремих символів і clean для перетворення рядків тексту (англ. string).

Правильний ідентифікатор складається з нуля або більше літер, підкреслень, дефісів, знаків питання та емодзі.

Якщо до функції clean передати порожній рядок тексту, вона має повернути порожній рядок тексту.

1. Замінити будь-які дефіси на підкреслення

Реалізуйте функцію transform, щоб замінювати будь-які дефіси на підкреслення.

julia> transform('-')
"_"

2. Видалити всі пробільні символи

Видаліть усі пробільні символи. Це стосується пробільних символів на початку й у кінці.

julia> transform(' ')
""

3. Перетворити camelCase на kebab-case

Змініть функцію transform, щоб вона перетворювала camelCase на kebab-case

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

4. Пропустити символи, які є цифрами

Змініть функцію transform, щоб вона пропускала будь-які символи, які є цифрами.

julia> transform('7')
""

5. Замінити грецькі малі літери на знаки питання

Змініть функцію transform, щоб вона замінювала будь-які грецькі літери в діапазоні від 'α' до 'ω'.

julia> transform('β')
"?"

6. Поєднати ці операції для роботи з рядком тексту

Реалізуйте функцію clean, щоб застосовувати ці операції до всього рядка тексту.

Символи, які не підпадають під ці правила, мають залишатися без змін.

julia> clean("  a2b Cd-ω😀  ")
"ab-cd_?😀"
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці
Julia Exercism

Час розпочати Бездоганно чисто?

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