{ "cells": [ { "cell_type": "markdown", "id": "088a86ab", "metadata": {}, "source": [ "# Числовые типы и классы\n", "\n", "Числовых типов много — это типы, которые реализуют класс Num:\n", "\n", "1. Double\n", "1. Float\n", "1. Int (Int8, Int16, ...)\n", "1. Integer\n", "1. RealFloat a => Num (Complex a) — это означает, что класс Complex будет являться числом, если выполняются некоторые условия на тип a. Условно, что с a можно работать как с вещественными числами. А вообще Complex — это число, составленное из двух частей, если мы пишем `2 :+ 3`, имеется в виду $2 + 3i$\n", "1. Integral a => Num (Ratio a)" ] }, { "cell_type": "code", "execution_count": 10, "id": "017bbbdc", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5.0 :+ 1.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import Data.Complex\n", "\n", "(2 :+ 3) * (1 :+ (-1)) -- (2+3i)(1-i) = 5+i" ] }, { "cell_type": "markdown", "id": "175acadf", "metadata": {}, "source": [ "Попробуем изучить, как устроен тип `Ratio`, он означает дроби типа 2/3 или 5/7, с ними можно производить арифметические операции. Значение этого типа создаётся с помощью конструктора `:%` (не доступен для нашего использования). Нам доступна функция (%), которая требует, чтобы оба аргумента были целыми числами (класс `Integral`)." ] }, { "cell_type": "code", "execution_count": 21, "id": "987ccd8d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2 % 3" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "1 % 2" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import Data.Ratio\n", "2 % 3\n", "1 % 3 + 1 % 6 -- 1/3 + 1/6 = 1/2" ] }, { "cell_type": "markdown", "id": "5c15cde4", "metadata": {}, "source": [ "Тип `type Rational = Ratio Integer`, т.е. отношение целых чисел реализует классы `Eq`, `Ord`, т.е. значения можно сравнивать:" ] }, { "cell_type": "code", "execution_count": 23, "id": "ad92dc9d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "1 % 2 < 2 % 3" ] }, { "cell_type": "markdown", "id": "d331b767", "metadata": {}, "source": [ "Еще `Rational` реализует класс `Num`, посмотрим, наконец, что можно делать с `Num`: `+`, `-`, `*`, `abs`, `signum`, `negate`, `fromInteger`:" ] }, { "cell_type": "code", "execution_count": 30, "id": "b71b4273", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(-2) % 3" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "1 % 1" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "42 % 1" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "85 % 2" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "negate $ 2 % 3\n", "signum $ (-2) % (-6) -- получаем 1 % 1, знак должен тоже иметь \n", "(fromInteger 42)::Rational -- можно рассмотреть результат как рациональное число\n", "-- (42::Integer) + (1 % 2) -- Integer и Rational не сложить вместе\n", "fromInteger (42::Integer) + (1 % 2) -- надо преобразовать с помощью fromInteger" ] }, { "cell_type": "markdown", "id": "986909b8", "metadata": {}, "source": [ "Кроме того `Rational` реализует классы `Real` и `RealFrac`. Посмотрим, что должны уметь значения типов, принадлежащих классу `Real`. Оказывается, для класса `Real` надо иметь класс `Num` (быть числом), `Ord` (быть упорядоченым) и реализовывать метод `toRational :: a -> Rational`." ] }, { "cell_type": "code", "execution_count": 31, "id": "f739e3c6", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2 % 3" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "toRational $ 2 % 3 -- для рациональных чисел превращение в рациональное ничего не делает" ] }, { "cell_type": "markdown", "id": "ce9982cc", "metadata": {}, "source": [ "Кто еще реализует `Real`: `Double`, `Float`, `Int`, `Integer`:" ] }, { "cell_type": "code", "execution_count": 43, "id": "31733fb4", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "42 % 1" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "5 % 8" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "4728779608739021 % 1125899906842624" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "884279719003555 % 281474976710656" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "toRational 42\n", "toRational 0.625 -- хранится без потери точности (в двоичной системе счисления = 0.конечное число)\n", "toRational 4.2\n", "toRational pi" ] }, { "cell_type": "markdown", "id": "ec1e7600", "metadata": {}, "source": [ "А что означает класс `RealFrac`: нужно реализовывать классы `Real` (уже изучили), `Fractional` (изучим следующим) и функции: `properFraction`, `truncate`, `round`, `ceiling`, `floor` (округление к 0, округление, округление вверх, округление вниз):" ] }, { "cell_type": "code", "execution_count": 49, "id": "6552e401", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(4,2 % 3)" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "5" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "properFraction :: forall a b. (RealFrac a, Integral b) => a -> (b, a)" ], "text/plain": [ "properFraction :: forall a b. (RealFrac a, Integral b) => a -> (b, a)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "properFraction $ 42 % 9 -- 4 + 2/3\n", "round $ 42 % 9 -- округление\n", ":type properFraction" ] }, { "cell_type": "markdown", "id": "b42e5cc1", "metadata": {}, "source": [ "Типы `Double` и `Float` тоже реализуют `RealFrac`, поэтому их тоже можно округлять:" ] }, { "cell_type": "code", "execution_count": 52, "id": "e0717b2c", "metadata": {}, "outputs": [ { "data": { "text/html": [ "