Julia - мова загального призначення, яку можна використовувати для більшості програмних завдань. На практиці ж основні випадки її застосування зосереджені в інженерії та науці. Швидкі, універсальні й витончені чисельні обчислення лежать в основі її дизайну.
Ціле число - це «кругле» число без десяткової крапки.
У концепції Основи ми бачили, що ціле значення можна присвоїти змінній, не вказуючи тип.
Для читабельності можна використовувати знаки підкреслення як роздільники цифр. Компілятор їх ігнорує.
julia> x = 3
3
julia> typeof(x)
Int64
julia> large_number = 1_234_567_890
1234567890
Усередині компілятор використовуватиме той тип цілого числа зі знаком, який найкраще підходить для конкретного процесора.
На сучасних ПК це зазвичай Int64, цілком достатній для більшості завдань.
Про типи докладніше йтиметься в одній із наступних концепцій. А поки що сподіваймося, що числові типи достатньо інтуїтивні, щоб цей документ був зрозумілим.
Як ми побачимо в одній із наступних концепцій, Julia нативно підтримує дуже великі багатовимірні масиви. Вони можуть випробовувати як обсяг доступної памʼяті, так і пропускну здатність для передавання даних.
Щоб дати програмісту тонший контроль, можна й досить часто варто вказати бажаний тип цілого числа зі знаком або без нього. Багатьом, хто вивчає Julia і раніше працював із Python, буде корисно порівняти це з NumPy, а не з базовим Python.
julia> y = Int8(42)
42
julia> typeof(y)
Int8
julia> z = UInt32(1024)
0x00000400
Зауважмо, що Julia типово показує цілі числа без знака у шістнадцятковому форматі, бо типи без знака часто використовують для низькорівневих порозрядних операцій.
Цілі числа також можна вводити у двійковому, вісімковому або шістнадцятковому вигляді, з префіксами 0b, 0o та 0x відповідно.
julia> a = 0x10
0x10
julia> Int(a)
16
julia> b = 0b1010 # displays as hexadecimal
0x0a
BigInt
Кожен тип цілого числа має максимальне й мінімальне значення, яке може зберігати:
julia> typemax(Int8)
127
julia> typemin(Int8)
-128
Вихід за межі цього допустимого діапазону спричинить переповнення цілого числа, а результати здаватимуться дуже дивними.
julia> c = Int8(126)
126
julia> c * c
4
Мабуть, ми погодимося, що 126 * 126 не повинно дорівнювати 4!
Докладне пояснення вимагає знання того, як регістри процесора зберігають цілі числа, а це виходить за межі цієї концепції.
Проблему можна зменшити, використовуючи «ширший» тип, аж до Int128 або UInt128 (що означає «ширший», розглянемо нижче). Однак усі вони мають жорсткі межі, перш ніж почнуться проблеми з переповненням.
Одне з рішень - скористатися типом BigInt, обмеженим лише памʼяттю компʼютера.
Це реалізація арифметики довільної точності у Julia.
julia> 2 ^ 70
0
julia> big = BigInt(2) ^ 70
1180591620717411303424
julia> UInt128(big)
0x00000000000000400000000000000000
Оскільки 2 у степені 70 не вміщується в Int64, перше обчислення в наведеному прикладі зазнає невдачі, хоч зауважмо: повідомлення про помилку відсутнє.
Будьмо пильними!
Приведення 2 до BigInt дає правильну відповідь, а показ у шістнадцятковому форматі ясніше дає зрозуміти, що це степінь двійки.
Не буде несподіванкою, що числа з плаваючою комою можуть мати десяткову крапку й дробову частину після неї.
julia> f = 3.45
3.45
julia> typeof(f)
Float64
Звісно, науковий запис підтримується.
julia> avogadro = 6.02e23
6.02e23
Як і з цілими числами, типового типу достатньо для більшості потреб, але є й інші типи зі знаком. Чисел із плаваючою комою без знака не існує.
Як скорочення, значення Float32 можна створити за допомогою суфікса f0.
julia> f32 = 4.56f0
4.56f0
julia> typeof(f32)
Float32
Інші розміри чисел із плаваючою комою потребують явного приведення, як і з цілими числами.
Максимальне й мінімальне значення можуть здивувати:
julia> typemax(Float64)
Inf
julia> typemin(Float64)
-Inf
Нескінченність - це допустиме значення!
Однак корисний діапазон чисел із плаваючою комою обмежений: усе дуже велике просто отримує значення Inf, а все дуже мале округлюється до 0.0.
Щоб побачити ці межі, можна скористатися іншою парою функцій: приблизно ± 10 ^ 308.
julia> floatmax(Float64)
1.7976931348623157e308
julia> floatmin(Float64)
2.2250738585072014e-308
Крім того, точність обмежена: подається велика, але скінченна кількість значущих цифр (приблизно 15 для Float64).
Як уже йшлося в концепції Основи, арифметичні оператори здебільшого працюють так само, як звичайна арифметика, якої нас учать у дитинстві.
Зауважмо, що піднесення до степеня використовує ^, а не ** (обидва поширені в інших мовах).
2 + 3 # 5 (addition)
2 - 3 # -1 (subtraction)
2 * 3 # 6 (multiplication)
8 / 2 # 4.0 (division)
8 % 3 # 2 (remainder)
2 ^ 3 # 8 (exponentiation)
Однак кілька деталей, специфічних для Julia, варті обговорення.
julia> x = 4.2
4.2
julia> 2 * x
8.4
julia> 2x
8.4
julia> 2.4x
10.08
Це може здивувати.
Завжди можна використати * як інфіксний оператор, як і в більшості інших мов програмування.
Однак Julia створювали люди, які вірять, що код має бути якнайбільше схожим на математичні рівняння.
Оскільки назви змінних мають починатися з літери, число (ціле або з плаваючою комою) перед назвою вважається неявним множенням.
Наприклад, якщо нам потрібна площа поверхні сфери, замість 4 * pi * r * r можна зробити так:
julia> surface(r) = 4π * r^2
surface (generic function with 1 method)
julia> surface(3)
113.09733552923255
Хоча π - вбудована константа, це також (грецька) літера.
Тому парсеру все одно потрібен один явний *, щоб відокремити π від r.
Використання / як інфіксного оператора завжди дає результат із плаваючою комою, навіть для цілих вхідних даних.
Для ділення цілих чисел є більше варіантів:
julia> 10 / 3
3.3333333333333335
julia> div(10, 3)
3
julia> 10 ÷ 3
3
julia> 10 // 3
10//3
Функція div() призначена для ділення цілих чисел; результат відкидається в бік нуля: вниз для додатних чисел, вгору для відʼємних.
Як синонім можна використати інфіксний оператор ÷, знову ж таки прагнучи зробити запис математичнішим.
Якщо ми користуємося редактором, який знає Julia, введімо \div і натиснімо клавішу <Tab>.
Оператор // потребуватиме окремої концепції пізніше в силабусі.
Поки що скажімо лише, що результат // - це «раціональне» число, формальна назва того, що більшість людей називає дробом.
Спільні множники скорочуються в чисельнику й знаменнику, щоб отримати відношення двох цілих чисел у тому, що називають «найпростішим виглядом».
julia> rationalnum = 22 // 6
11//3
julia> typeof(rationalnum)
Rational{Int64}
Маємо раціональні числа. А що щодо «ірраціональних» чисел?
julia> π
π = 3.1415926535897...
julia> typeof(π)
Irrational{:π}
Ірраціональне число - те, яке не можна звести до відношення цілих чисел.
Поширені приклади: π, e (число Ейлера) та багато коренів, як-от √2 (квадратний корінь із 2).
Julia намагається робити математику як слід.
Часто це відбувається автоматично:
julia> x = 2 + 3.5
5.5
julia> typeof(x)
Float64
Ми додали Int64 до Float64 й отримали результат Float64.
Насправді ціле число було непомітно перетворене на Float64 ще до додавання.
У Julia є поняття «ширини» числових типів.
Int64 ширший за Int16.Якщо у виразі використовується суміш типів, кожен із них за потреби «підвищується» до найширшого використаного типу.
Щоб змусити перетворення, ми можемо привести ціле число до конкретного типу, як у Float64(5).
Або ж просто скористаймося float(5) і дозвольмо компілятору вибрати відповідний тип.
Перетворення з чисел із плаваючою комою на цілі неминуче складніші. Що робити з тим, що стоїть після десяткової крапки?
round() перетворює до найближчого цілого; у разі рівновіддаленості, як-от 4.5, округлення йде до найближчого парного цілого.floor() округлює вниз, ceil() округлює вгору, trunc() округлює в бік нуля.Int32(), завершиться невдачею з InexactError.Однак типово ці функції повертають не той тип цілого числа, який міг бути потрібен. Бажаний тип результату можна вказати.
julia> round(4.5)
4.0
julia> round(Int64, 4.5)
4
julia> round(Int, 4.5) # => default integer type
4
julia> ceil(Int16, 4.3)
5
Округлення до заданої кількості цифр після десяткової крапки теж можливе за допомогою ключового слова digits.
julia> round(π, digits=10)
3.1415926536
Докладніше див. у документації.
Невже це просто спричиняє помилку? Насправді все не так просто.
Ділення цілих чисел за допомогою ÷ або // спричинить помилку, як і слід було очікувати.
Ділення чисел із плаваючою комою за допомогою / дотримується радше інженерного підходу, ніж стандартного підходу інформатики:
julia> 2 / 0
Inf
julia> 0 / 0
NaN
Як уже йшлося в попередньому розділі, нескінченність - це допустиме число з плаваючою комою в Julia, позначуване як Inf.
Коли чисельник теж дорівнює нулю, результат математично не визначений.
Тоді Julia трактує його як «не число», позначуване як NaN.
Якщо це здається дивним, подумаймо про це в контексті роботи з великими масивами реальних (а отже, часто досить неохайних) даних. Щоб рухатися далі, найкраще просто позначити проблемні значення й іти далі.
Нескінченна ручна перевірка значень була б стомливою для програмування і неодмінно зашкодила б продуктивності під час виконання.
Зупинка з повідомленням про помилку за кожного найменшого збою зробила б нашу програму вкрай непопулярною серед користувачів!
Як описано в концепції Conditionals, перевірку на рівність зазвичай виконують оператором ==.
Це добре працює для цілих чисел, символів, рядків тексту (англ. string) тощо.
Однак значення з плаваючою комою мають обмежену точність, і різні способи обчислення того самого результату можуть давати трохи різні значення.
Для Float64 це зазвичай десь на 15-й значущій цифрі: невелика різниця, але не «рівність».
Традиційно програмістам радять ніколи не використовувати == зі значеннями з плаваючою комою: результати непередбачувані.
Широко вживана альтернатива - перевіряти абсолютне значення різниці проти деякого допустимого відхилення (часто його називають епсилон, або ϵ).
Тож замість a == b використовуймо abs(a - b) < epsilon.
Julia пропонує чистішу альтернативу, функцію isapprox().
Для абсолютного відхилення синтаксис такий: isapprox(a, b, atol=epsilon).
У цьому випадку ключове слово atol обовʼязкове.
Відносне відхилення часто корисніше.
Воно типове, тож isapprox(a, b) спробує вибрати якесь розумне значення для rtol (правила досить складні).
Відносне відхилення теж можна вказати як частку від значень, що порівнюються.
Тож isapprox(a, b, rtol=0.01 перевіряє, що значення відрізняються не більш ніж на 1%.
У звичному для Julia стилі є математичний оператор, синонім типового випадку: a ≈ b (оператор вводять як \approx, а потім <tab>).
Крім раціональних чисел, у наступних частинах силабусу йтиметься про:
2.3 + 4.5im.