Математические функции
В библиотеке Java определено большое количество математических функций, предназначенных для выполнения более сложных операций. Для примера их использования рассмотрим задачу решения квадратного уравнения ax2 + bx + c = 0.
Напомним, что корни квадратного уравнения ищутся по формуле x1,2 = (-b ± √d) / (2a), где d — дискриминант квадратного уравнения — вычисляется как d = b2-4ac. Мы решим эту задачу в упрощённом виде — найти какой-нибудь из двух возможных корней, скажем, тот, в котором в числителе используется знак плюс.
Для начала напишем функцию для расчёта дискриминанта (она ещё пригодится нам в будущем). Для расчёта b2применим уже написанную выше функцию sqr(x: Double)
.
fun discriminant(a: Double, b: Double, c: Double) = sqr(b) - 4 * a * c
В приведённой записи b
является аргументом функции sqr
. Запись вида sqr(b)
называется вызовом функции sqr
. Подчеркнём отличие параметра и аргумента — параметр определяется внутри функции и имеет определённое имя, в данном случае x
, а аргумент передаётся в функцию снаружи и может являться как именем переменной, так и более сложным выражением.
Теперь напишем функцию для поиска корня квадратного уравнения. Для вычисления квадратного корня применим готовую математическую функцию sqrt(x: Double)
из математической библиотеки Котлина.
fun quadraticEquationRoot(a: Double, b: Double, c: Double) = (-b + kotlin.math.sqrt(discriminant(a, b, c))) / (2 * a)
Здесь мы, в свою очередь, используем уже написанную функцию discriminant
для поиска дискриминанта, и выражение discriminant(a, b, c)
, то есть дискриминант уравнения, является аргументом функции sqrt
. Это как раз тот случай, когда аргумент является сложным выражением.
Обратите внимание на нотацию kotlin.math.
перед именем функции sqrt
. Поскольку готовых функций существует очень много, они разбиты на так называемые пакеты и классы внутри пакетов. kotlin.math.sqrt
является полнымименем функции вычисления квадратного корня, а sqrt
— её коротким именем. Из-за неудобства работы с полными именами, чаще используется следующая запись:
// Разрешение использовать короткие имена для ВСЕХ функций из пакета kotlin.math import kotlin.math.* fun quadraticEquationRoot(a: Double, b: Double, c: Double) = (-b + sqrt(discriminant(a, b, c))) / (2 * a)
Здесь import — так называемая директива импорта имён, смысл её пояснён в комментарии.
Примеры других функций из kotlin.math
:
- abs(x: Int) или abs(x: Double) — модуль;
- sqrt(x: Double) — квадратный корень;
- Double.pow(x: Double) — возведение в степень xy, используется как
x.pow(y)
; - sin(x: Double) — синус, cos(x: Double) — косинус, tan(x: Double) — тангенс, все три функции считают, что
x
задан в радианах; - exp(x: Double) — экспонента ex;
- log(x: Double), log10(x: Double) — соответственно натуральный и десятичный логарифм;
- min(x: Int, y: Int) или min(x: Double, y: Double) — минимум из двух чисел;
- max(x: Int, y: Int) или max(x: Double, y: Double) — максимум из двух чисел.
В том же пакете kotlin.math
определены константы PI = 3.14…
и E = 2.718…
Переменные в функциях
Выше мы рассмотрели примеры с функциями sqr
, discriminant
и sqRoot
, вычисление результата в которых занимало одну строчку кода. Однако, в программировании это скорее редкий случай; гораздо чаще расчёт результата функции предполагает реализацию некоторой последовательности вычислений — алгоритма. Для сохранения результатов промежуточных вычислений программисты придумали переменные.
Рассмотрим, например, задачу вычисления произведения двух корней квадратного уравнения. Напомним, что корни квадратного уравнения вычисляются как (-b+√d)/(2a) и (-b-√d)/(2a) соответственно, где d — дискриминант квадратного уравнения. При вычислении произведения удобно вначале сохранить вычисленный корень из дискриминанта в переменной sd
, так как он используется при вычислении обоих корней. Затем нужно вычислить оба корня x1
и x2
и уже потом рассчитать их произведение. На Котлине это записывается следующим образом:
fun quadraticRootProduct(a: Double, b: Double, c: Double): Double /* тип обязателен */ { // Тело функции в виде блока val sd = sqrt(discriminant(a, b, c)) val x1 = (-b + sd) / (2 * a) val x2 = (-b - sd) / (2 * a) return x1 * x2 // Результат }
В этом примере тело функции записано в виде блока в фигурных скобках, в противоположность телу в виде выражения — как в функциях sqr
и discriminant
выше. Знак равенства при этом убирается и обязательно указывается тип результата функции. В примере присутствуют три промежуточные переменные — d
, x1
, x2
. Определение промежуточной переменной в Котлине начинается с ключевого слова val (сокращение от value — значение), за которым следует имя переменной и, после знака равенства — её значение. При желании можно также указать тип переменной, например:
// ... val sd: Double = sqrt(discriminant(a, b, c))
Если тип переменной не указан, он определяется автоматически, например, в данном случае он совпадёт с типом результата функции sqrt
.
Блок состоит из так называемых операторов (в примере их четыре), выполняющихся по порядку сверху вниз. Преждечем использовать какую-либо переменную, её следует определить. Например, такая запись привела бы к ошибке:
fun quadraticRootProduct(a: Double, b: Double, c: Double): Double { val x1 = (-b + sd) / (2 * a) // Unresolved reference: sd val x2 = (-b - sd) / (2 * a) // Unresolved reference: sd val sd = sqrt(discriminant(a, b, c)) return x1 * x2 // Результат }
Последний оператор функции, начинающийся с ключевого слова return, определяет значение её результата; returnпереводится с английского как вернуть (результат). Функция quadraticRootProduct
в первую очередь вычислит значение переменной sd
, используя другие функции discriminant
и sqrt
. Затем произойдёт вычисление переменных x1
и x2
и лишь в конце — вычисление результата в операторе return.
Для сравнения, приведём запись той же функции, не использующей переменные:
fun quadraticRootProduct(a: Double, b: Double, c: Double) = ((-b + sqrt(discriminant(a, b, c))) / (2 * a)) * ((-b - sqrt(discriminant(a, b, c))) / (2 * a))
Хотя и записанная в одну строчку, такая функция является гораздо менее понятной, при её написании легко запутаться при расстановке скобок. Кроме того, в ней происходит двухкратное вычисление корня из дискриминанта, чего следует избегать.
Примеры с квадратными уровнениями отталкивают от изучения. как буд то программирование == математика
разве не sqr(x)= x^1/2 ?