Досі в силабусі майже не йшлося про типи, але очевидно, що в Julia вони є:
julia> vals = (42, 4.3, π, "hello", 'Q')
(42, 4.3, π, "hello", 'Q')
julia> typeof(vals)
Tuple{Int64, Float64, Irrational{:π}, String, Char}
Ми жодного разу не вказали типи, але Julia все одно їх присвоїла.
types, які відіграють центральну роль у її дизайні.Type Inference.JIT-компілятор перегляне (увесь) код, побачить, як використовується змінна, і виведе відповідний типовий тип, сумісний із цим використанням.
Покладатися на виведення типів цілком доречно, коли ми розвʼязуємо прості навчальні вправи, але для більших програм нам, найімовірніше, знадобиться точніший контроль.
На більшості сучасних процесорів ціле число типово має тип Int64.
У концепції Numbers ми бачили, що значення можна перетворити на певний тип, відмінний від типового.
julia> x = Int16(42)
42
julia> typeof(x)
Int16
Однак змінній x усе ще можна присвоїти значення іншого типу:
julia> x = "changed"
"changed"
julia> typeof(x)
String
Це type instability, тобто:
Натомість ми можемо задати тип x за допомогою оператора :::
julia> y::Int16 = 42
42
julia> typeof(y)
Int16
julia> y = "changed"
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int16
The function `convert` exists, but no method is defined for this combination of argument types.
Тепер y має тип Int16 і завжди його матиме.
Отже, компілятор знає, скільки байтів зарезервувати для неї, і може оптимізувати решту коду, покладаючись на стабільний тип.
У цьому сенсі змінна більш-менш схожа на змінні у статично типізованій мові, як-от C.
Int64, Int16, String, Char: звідки ж ці типи беруться?
У багатьох обʼєктно-орієнтованих (ООП) мовах кожен тип є класом, а підкласи шикують їх в ієрархію класів, і методи класів визначають поведінку.
Java і Ruby - очевидні приклади цього підходу, але навіть Python усередині влаштований схоже.
У Julia немає класів.
Задокументована причина полягає в тому, що можливості ООП заважають JIT-компілятору та шкодять швидкодії під час виконання.
І попри це, погляньмо на цей код:
julia> y::Int16 = 42
42
julia> typeof(y)
Int16
julia> supertype(Int16)
Signed
julia> supertypes(Int16)
(Int16, Signed, Integer, Real, Number, Any)
Додамо трохи деталей:
Int16 - це тип, і ми можемо створювати змінні цього типу.Int16 - підтип Signed, і функція supertype() показує нам це.Integer, Real і Number до Any на самому верху, і subtypes() виведе для нас цю гілку ієрархії.Усі гілки закінчуються на Any, який унікальний тим, що є власним надтипом.
julia> supertypes(String)
(String, AbstractString, Any)
julia> supertype(Any)
Any
Отже, у Julia немає ієрархії class, але ієрархія type у неї таки є.
Згодом ми спробуємо розібратися, як це працює, але спершу нам належить дослідити набагато більше.
Ми можемо використовувати typeof(), щоб перевіряти рівність звичним способом.
julia> typeof(11)
Int64
julia> typeof(11) == Int64
true
julia> typeof(11) == Number
false
Рівність типів має бути точною, адже таке порівняння нічого не знає про ієрархію типів.
Гнучкіше: isa скаже нам, чи має значення той самий тип, що й те, з чим ми порівнюємо, або його підтип.
Його можна використовувати і як функцію, і в інфіксній формі.
julia> 12 isa Int64
true
julia> 12 isa Number
true
julia> isa(12, Number)
true
julia> 12 isa String
false
Звернімо увагу: isa приймає value ліворуч, а не type.
Спроба порівняти в такий спосіб два типи дасть несподівані результати.
Правильний оператор - <:, і ми ще не раз побачимо його в наступних концепціях.
julia> Int64 isa Number ## Don't do this!
false
julia> Int64 <: Number
true
Ми бачили, що ієрархія типів утворює деревоподібну структуру (у сенсі інформатики, з коренем угорі).
Кожен елемент дерева - це node, і їх можна поділити на категорії:
abstract.concrete.julia> subtypes(Integer) # an abstract type
3-element Vector{Any}:
Bool
Signed
Unsigned
julia> subtypes(Int64) # a concrete type
Type[]
Це важлива відмінність, адже лише конкретні типи можуть бути instantiated як змінні.
julia> a::Int16 = 42
42
julia> typeof(a)
Int16
julia> b::Integer = 42
42
julia> typeof(b)
Int64
Звернімо увагу: спроба використати абстрактний тип (у цьому випадку) не видає жодного повідомлення про помилку, але компілятор створює відповідний конкретний тип: Int64 замість Integer.
У цій вправі ми трохи займемося інженерією даних, попередньо обробивши деякі дані з оцінками. Школа, у якій навчається дуже, дуже, дуже багато (забагато?) студентів, намагається проаналізувати отримані студентами оцінки й хоче зробити це ефективно, тож ідея в тому, щоб зменшити обсяг даних і впорядкувати їх.
Шкала оцінок зазвичай від 0 до 10, тож найефективніше використовувати тип даних, який займає лише вісім бітів на одну оцінку.
UInt8.Int8.Коли ми реалізуємо функцію для перетворення оцінок, потрібно буде написати ще одну, яка оброблятиме колекції, у яких зберігаються оцінки, знижуючи їх і повертаючи у спадному порядку.
Vector, відсортовані за зростанням, щоб зберігати оцінки, бо вони прискіпливо ставляться до своїх даних.Set, які не відсортовані, щоб зберігати оцінки, бо вони трохи ледачіші.В обох функціях нам потрібно буде обробити некоректні вхідні дані, викинувши MethodError.
Обробку винятків буде розглянуто в пізнішому концепті, тож для цієї вправи можна скористатися таким синтаксисом:
throw(MethodError(f, args))
де f - це функція, а args - кортеж аргументів, переданих у функцію.
Для функції demote(n) f - це demote, а args - це (n,):
throw(MethodError(demote, (n,)))
Реалізуйте метод demote(n).
Float64, вона має округлити число вгору до найближчого цілого й повернути тип даних UInt8.Integer, вона має повернути те саме ціле число з типом даних Int8.julia> demote(4.2)::UInt8
5
julia> demote(4)::Int8
4
julia> demote("hi")
MethodError: no method matching demote(::String) #output truncated
Реалізуйте метод preprocess(coll).
Vector, вона має знизити всі числа й перевернути вектор.Set, вона має знизити всі числа й повернути відсортований вектор у спадному порядку.julia> preprocess([1, 2, 3])
3-element Vector{Int8}:
3
2
1
julia> preprocess(Set([2.2, 5.8, 3.4]))
3-element Vector{UInt8}:
6
4
3
julia> preprocess(42)
MethodError: no method matching preprocess(::Int64) #output truncated
Зареєструйтеся на Exercism, щоб вивчати й опановувати Julia, а також 35 концепцій128 вправ та справжнє наставництво від людей, і все це безкоштовно.