Магия размерностей и магия Котлина. Часть вторая: Продвинутые возможности  KotUniL

5477170413c96d2b7ff7a643a815ae96.png

Это вторая статья из серии статей про магии размерностий и Котлина на примере библиотеки KotUniL.

В первой статье мы рассмотрели, как с помощью этой библиотеки не разбивать космические аппараты :-). 

В этой статье мы рассмотрим менее очевидные, но не менее интересные фичи библиотеки.

Вот список всех статей серии:

  1. Магия размерностей и магия Котлина. Часть первая: Введение в KotUniL  

  2. Магия размерностей и магия Котлина. Часть вторая: Продвинутые возможности  KotUniL

  3. Магия размерностей и магия Котлина. Часть третья: Смешение магий (будет скоро опубликована)

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 ^ 2)

pascal

Pa

kg/(m * (s ^ 2))

joule

J

kg (m2)/(s ^ 2)

watt

W

kg*(m2)/(s ^ 3)

coulomb

C

sA

volt

V

kgm2*(s ^ -3) * (A ^ -1)

farad

F

(kg  ^ -1) * (m  ^ -2) * (s ^ 4) * (A ^ 2)

ohm

Ω

kgm2 * (s ^ -3) * (A ^ -2)

siemens

S

(kg ^ -1) * (m ^ -2) (s ^ 3) (A ^ 2)

weber

Wb

kg (m2) * (s ^ -2) * (A ^ -1)

tesla

T

kg* (s ^ -2) * (A ^ -1)

henry

H

kg* (m2)(s ^ -2)(A ^ -2)

degreeCelsius

Celsius

(K ^ 1)

lumen

lm

((cd ^ 1)sr)

lux

lx

cdsr*(m ^ -2)

becquerel

Bq

(s ^ -1)

gray

Gy

(m2)(s ^ -2)

sievert

Sv

(m2)(s ^ -2)

katal

kat

(mol * (s ^ -1))

Не удивляйтесь, если вы встретите в этой таблице и в библиотеке 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, , Polish złoty

ThaiBaht, THB, ฿, Thai bahtIsraeli

NewShekel, ILS, Israeli new shekel

IndonesianRupiah, IDR, Rp, Indonesian rupiah

CzechKoruna, CZK, , 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

© Habrahabr.ru