Система основных измерений в физике
Система основных измерений в физике

Это вторая статья из серии статей про магии размерностий и Котлина на примере библиотеки 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.35 га. Во время дождя с неба выпало 1 мм воды.

Если бы дождя не было, парк нужно было бы поливать водой из автомобильных цистерн. Одна автомобильная цистерна вмещает 4 тонны воды. Сколько цистерн необходимо для достижения того же эффекта, что и в случае дождя? Напоминание: плотность воды составляет 1 кг/л

val s = 2.35ha
val ω = s*1.mm // Суммарный объём упавшей с неба воды
val ρ = kg/l // Плотность воды
val τ = ω * ρ // Общий вес дождевой воды
val n = τ/4.t
assertEquals(5.875, 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

Комментарии (7)


  1. WebConn
    16.12.2022 00:35
    -1

    Очень важно: символ backtick (`) (UTF-8 код U+0060) не равен символам апострофа на клавиатуре (UTF-8 код U+0027).

    Напомнило: The Worst Programming Language Ever


    1. visirok Автор
      16.12.2022 01:01
      +1

      Просто ассоциация, или Вы имеете ввиду что-то конкретное? Если да - то с какой минуты начинается релевантная часть видео?


      1. WebConn
        16.12.2022 18:44

        Там было про символы, которые нельзя ввести с клавиатуры (если не ошибаюсь, про пробелы и как раз разные кавычки). По-моему странное решение использовать в ЯП символ, которые не просто не вводится с клавиатуры, но ещё и внешне похож на тот, который вводится.


        1. visirok Автор
          16.12.2022 19:26

          Не берусь судить о мотивах создателей языка. Но их можно понять. Это должны были быть "кавычки", окаймляющие тело идентификатора. Обычные кавычки и апострофы уже были в языке задействованы.

          Думаю, в итоге получилось неплохо. Это не самая используемая фича, поэтому для её применения необходимо сделать особенный реверанс. Но зато нет опасений, что кто-то случайно напишет и будет трудно понять, в чём дело. Как это было с запятой в С.


        1. janatem
          17.12.2022 19:30

          Вообще-то backtick — это обычный обратный апостроф, который есть на стандартной клавиатуре (в левом верхнем углу). Также он является ASCII-символом, о чем нетрудно догадаться, взглянув на его юникод-представление, — значение входит в первые 128 символов (0x60 < 0x80 = 128).


          1. janatem
            17.12.2022 19:34

            Поэтому удобней не следовать этому совету

            Чтобы использовать его, лучше скопировать его непосредственно из этого примера.

            а набирать на клавиатуре (только не перепутав с обычным апострофом).


          1. visirok Автор
            17.12.2022 19:41

            Да, вы правы. Я пользовался нестандартной клавиатурой. Текст я поправлю.