Магия размерностей и магия Котлина. Часть вторая: Продвинутые возможности KotUniL
Это вторая статья из серии статей про магии размерностий и Котлина на примере библиотеки KotUniL.
В первой статье мы рассмотрели, как с помощью этой библиотеки не разбивать космические аппараты :-).
В этой статье мы рассмотрим менее очевидные, но не менее интересные фичи библиотеки.
Вот список всех статей серии:
Магия размерностей и магия Котлина. Часть первая: Введение в KotUniL
Магия размерностей и магия Котлина. Часть вторая: Продвинутые возможности KotUniL
Магия размерностей и магия Котлина. Часть третья: Смешение магий (будет скоро опубликована)
Pretty print
Начнём мы с красивой распечатки результатов наших расчётов. В стандарте системы СИ на этот счёт есть строгие рекомендации, которые библиотека пытается в меру своих возможностей выполнять:
val s = 4.m * 5.m
assertEquals("20 m2", s.show("%.0f"))
val x = 20.l
val format = "%.2f"
assertEquals("0,02 m3", x.show(format))
val h = x/s
assertEquals("0,001 m", h.show("%.3f"))
val y = 3.1415927.m
assertEquals("3,142 m", y.show("%.3f"))
Способы задания базовых единиц СИ
Как известно, система СИ определяет ровно 7 физических единиц, с помощью которых удаётся измерять и моделировать все объекты и процессы нашего мира.
Вот этот список:
SI Base Unit | Unit symbol |
---|---|
second | s |
metre | m |
kilogram | kg |
ampere | A |
kelvin | K |
mole | mol |
candela | cd |
Рассмотрим, как можно задавать в KotUniL физические величины на примере одной секунды (равенства внизу показывают разные способы физической величина «размером» в одну секунду):
s == 1*s
1*s == 1.0.s
Second(1.0) == 1.s
Производные единицы СИ
Стандарт СИ определяет кроме семи базовых единиц также 22 производные единицы. Они перечислены в таблице ниже.
SI Derived Unit | Unit symbol | Formula |
---|---|---|
radian | rad | m/m |
steradian | sr | m2/m2 |
hertz | Hz | 1/s |
newton | N | kgm/(s |
pascal | Pa | kg/(m * (s |
joule | J | kg (m2)/(s |
watt | W | kg*(m2)/(s |
coulomb | C | sA |
volt | V | kgm2*(s |
farad | F | (kg |
ohm | Ω | kgm2 * (s |
siemens | S | (kg |
weber | Wb | kg (m2) * (s |
tesla | T | kg* (s |
henry | H | kg* (m2)(s |
degreeCelsius | Celsius | (K |
lumen | lm | ((cd |
lux | lx | cdsr*(m |
becquerel | Bq | (s |
gray | Gy | (m2)(s |
sievert | Sv | (m2)(s |
katal | kat | (mol * (s |
Не удивляйтесь, если вы встретите в этой таблице и в библиотеке KotUniL необычные для идентификаторов символы типа Ω. Это возможно потому, что согласно спецификации в идентификаторах Kotlin могут использоваться самые распространенные символы юникода (см. параграф »1.2.4 Идентификаторы» в спецификации Kotlin). Производные единицы можно задавать и использовать так же, как и базовые. Переменных из обеих групп можно использовать в формулах путем умножения или деления.
Не удивляйтесь, когда увидите символ ^. В Котлине нет оператора для возведения в степень (взятия корня), но есть функция pow(a, b). KotUniL был расширен инфиксной функцией с той же семантикой. К сожалению, с помощью этого трюка нельзя установить правильный приоритет для этой функции. Хотя она выглядит как «настоящий» оператор, для сохранения правильного приоритета операции ее нужно заключить в круглые скобки.
Чтобы поближе разобраться с программированием производных единиц рассмотрим реальный пример.
Молодожёны Саша и Наташа решили выехать на природу. Они взяли с собой солнечную батарею. По приезду на место Саша сразу включили ее. Солнечная батарея работала 2 часа производила 12 вольт при 7 ампер. Выработанная электроэнергия хранилась в переносном аккумуляторе. Эффективность хранения составила 85%. После этого Наташа решила приготовить чай в кипятильнике. Для приготовления горячей воды для чая с помощью чайника мощностью 500 ватт воду нужно кипятить 8 минут. Вопрос, достаточно ли для этого накопленной в аккумуляторе электроэнергии?
Напишем простенький код с использованием KotUniL:
val producedElectricity = 12.V * 7.A * 2.h
val savedElectricity = producedElectricity * 85.`%`
val neededElectricity = 0.5.kW * 8.min
val dif = savedElectricity - neededElectricity
assertTrue(dif < 0.W*h) //Сравнение единиц KotUniL
assertTrue(dif.value < 0) //Сравнение безразмерных величин
Обратите внимание, что для удобства мы используем здесь символ %. Это также является расширением Kotlin в нашей библиотеке.
Что касается этого и ряда других подобных расширений, рассмотрим цитату из спецификации: «Kotlin поддерживает способ задания идентификаторов, заключая любую последовательность символов в символы обратного знака (`), что позволяет использовать любое имя в качестве идентификатора.» (См. параграф 1.2.4 «Идентификаторы» в спецификации Kotlin).
Очень важно: символ backtick (`) (UTF-8 код U+0060) не равен символам апострофа на клавиатуре (UTF-8 код U+0027). Чтобы использовать его, лучше скопировать его непосредственно из этого примера.
Большинство производных единиц могут быть получены из базовых единиц или из других производных единиц различными способами.
Рассмотрим пример Tesla:
assertEquals(T, kg * (s `^` -2) * (A `^` -1))
assertEquals(T, Wb/m2)
Собственные производные единицы
С помощью KotUniL вы можете определить свои собственные производные единицы.
Рассмотрим не вполне научно обоснованный пример. Представим, что скорость таяния снега в горах пропорциональна продолжительности и температуре выше 0 °C и равняется 10 микрон в час на градус. Это будет наша новая производная единица. Если текущая толщина снега составляет 10 см, то какая его часть растает за 5 часов при температуре 20 °C?
Приведенный ниже код также демонстрирует приятную сторону Kotlin — возможность использования символов Unicode, например, греческих букв.
val ζ = 10.μm/(h*K) //скорость таяния льда
val τ = 10.cm
val t = 20.`°C`
val ξ = 5.h*(t - 0.`°C`)
val σ = ζ*ξ //уровень стаявшего снега
val α = σ/τ //отношения стаявшего снега к исходному
assertEquals(1.0, α.`as %`, ε) // Представление отношения в процентах
Обратите внимание, как можно представить результат расчета в процентах: α.`as %`- отношение, представленное в процентах.
ε — некоторое небольшое значение для допуска при сравнении двойных чисел. Это тоже одна из определённых в KotUniL переменных.
Префиксы системы СИ
Система СИ определяет префиксы, которые мы с детства привыкли употреблять, часто не задумываясь. Размеры комнаты мы измеряем в «чистых» метрах, размеры смартфонов в миллиметрах, а расстояния между городами в километрах.
В зависимости от предметного домена часто удобно работать не с чистыми, а с префиксами единицами. Каждый префикс имеет своё имя и сокращённое обозначение в виде символа.
В KotUniL реализованы все предусмотренные стандартом Си префиксы. Вот их полная таблица.
Prefix | Symbol | Degree |
---|---|---|
quetta | Q | 30 |
ronna | R | 27 |
yotta | Y | 24 |
zetta | Z | 21 |
exa | E | 18 |
peta | P | 15 |
tera | T | 12 |
giga | G | 9 |
mega | M | 6 |
kilo | k | 3 |
hecto | h | 2 |
deca | da | 1 |
deci | d | -1 |
centi | c | -2 |
milli | m | -3 |
micro | μ | -6 |
nano | n | -9 |
pico | p | -12 |
femto | f | -15 |
atto | a | -18 |
zepto | z | -21 |
yocto | y | -24 |
ronto | r | -27 |
quecto | q | -30 |
В приведенном ниже примере мы проверяем, что один километр равен миллиарду микрометров.
val d = km - (10 `^` 9) * μm
assertTrue(abs(d.value) < ε)
Нестандартные единицы
Стандарт СИ признаёт в качестве допустимых ряд единиц, которые исторически отвоевали себе место в практической деятельности людей, такие как минуты, часы и дни для времени, квадратные и кубические метры и т.д.
Таблица внизу даёт представление об этих величинах.
Пример:
Городской парк имеет площадь 2,3 га. Во время дождя с неба выпало 1 мм воды.
Если бы дождя не было, парк нужно было бы поливать водой из автомобильных цистерн. Одна автомобильная цистерна вмещает 4 тонны воды. Сколько цистерн необходимо для достижения того же эффекта, что и в случае дождя? Напоминание: плотность воды составляет 1 кг/л
val s = 1.ha
val ω = s*100.mm // Суммарный объём упавшей с неба воды
val ρ = kg/l // Плотность воды
val τ = ω * ρ // Общий вес дождевой воды
val n = τ/4.t
assertEquals(250.0, n.value, ε)
Добавляем валюты
Чтобы расширить сферу применения библиотеки, в неё наряду с физическими единицами в качестве самостоятельных единиц добавлены тридцать самых популярных валют мира.
Вот этот список
UnitedStatesDollar, USD, US$
, United States dollar
Euro, EUR, €
, Euro
JapaneseYen, JPY, ¥
, Japanese yen
PoundSterling, GBP, £
, Sterling
Renminbi, CNY, 人民币
, Renminbi
AustralianDollar, AUD, A$
, Australian dollar
CanadianDollar, CAD, C$
, Canadian dollar
SwissFranc, CHF, SCHF, Swiss franc //To avoid name conflict for CHF
HongKongDollar, HKD, HK$
, Hong Kong dollar
SingaporeDollar, SGD, S$
, Singapore dollar
SwedishKrona, SEK, skr, Swedish krona //To avoid name conflict for symbol 'kr'
SouthKoreanWon, KRW, ₩
, South Korean won
NorwegianKrone, NOK, nkr, Norwegian krone //To avoid name conflict for symbol 'kr'
NewZealandDollar, NZD, NZ$
, New Zealand dollar
IndianRupee, INR, ₹
, Indian rupee
MexicanPeso, MXN, $
, Mexican peso
NewTaiwanDollar, TWD, NT$
, New Taiwan dollar
SouthAfricanRand, ZAR, R, South African rand
BrazilianReal, BRL, R$
, Brazilian real
DanishKrone, DKK, dkr, Danish krone //To avoid name conflict for symbol 'kr'
PolishZłoty, PLN, zł
, Polish złoty
ThaiBaht, THB, ฿
, Thai bahtIsraeli
NewShekel, ILS, ₪
Israeli new shekel
IndonesianRupiah, IDR, Rp, Indonesian rupiah
CzechKoruna, CZK, Kč
, Czech koruna
UAEDirham, AED, Dh, UAE dirham
TurkishLira, TRY, ₺
, Turkish lira
HungarianForint, HUF, Ft, Hungarian forint
ChileanPeso, CLP, CLP﷼ , Saudi riyal
PhilippinePeso, PHP, ₱, Philippine peso
MalaysianRinggit, MYR, RM, Malaysian ringgit
ColombianPeso, COP, COL, Colombian peso
RussianRuble, RUB, ₽, Russian ruble
RomanianLeu, RON, L, Romanian leu
И снова, уже последний пример:
Владелец дома Вова решил покрыть пол плиткой в одной из своих комнат.
Он купил 16,5 кв. м плитки по цене 52 €/кв. м. Сколько он заплатил за плитку?
val prise = 52.`€`/m2
val s = 16.5*m2
val cost = s*prise
assertEquals("858,00 EUR", cost.show("%.2f"))
assertEquals("EUR", cost.unitSymbols())
Ну вот мы и познакомились с продвинутыми фичами библиотеки KotUniL.
Напомню, что способ её подключения в ваш проект описан в предыдущей статье серии.
В следующей, последней статье серии я попытаюсь рассказать о некоторых поразивших меня «магических» особенностях системы СИ и их влиянии на дизайн библиотеки.
Иллюстрация: Система основных измерений в физике, основанная на математическом манипулировании длиной, временем и массой. Источник: Wikipedia