52700.fb2 Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 205

Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 205

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

просто f + xF 1, без скобок для аргумента.

Определим вспомогательные функции для создания рядов:

p0 :: Num a => a -> Ps a

p0 x = x :+: p0 0

ps :: Num a => [a] -> Ps a

ps []

= p0 0

ps (a:as) = a :+: ps as

Обратите внимание на то, как мы дописываем бесконечный хвост нулей в конец ряда. Теперь давайте

определим функцию вычисления ряда. Мы будем вычислять лишь конечное число степеней.

eval :: Num a => Int -> Ps a -> a -> a

eval 0 _

_ = 0

eval n (a :+: p) x = a + x * eval (n-1) p x

В первом случае мы хотим вычислить ноль степеней ряда, поэтому мы возвращаем ноль, а во втором

случае значение ряда a+ xP складывается из числа a и значения ряда P умноженного на заданное значение.

Степенные ряды | 183

Арифметика рядов

В результате сложения и умножения рядов также получается ряд. Также мы можем создать ряд из числа.

Эти операции говорят о том, что мы можем сделать степенной ряд экземпляром класса Num.

Сложение

Рекурсивное представление ряда f + xF позволяет нам очень кратко выражать операции, которые мы

хотим определить. Теперь у нас нет бесконечного набора коэффициентов, у нас всего лишь одно число и ещё

один ряд. Операции существенно упрощаются. Так сложение двух бесконечных рядов имеет вид:

F + G = ( f + xF 1) + ( g + xG 1) = ( f + g) + x( F 1 + G 1)

Переведём на Haskell:

(f :+: fs) + (g :+: gs) = (f + g) :+: (fs + gs)

Умножение

Умножим два ряда:

F ∗ G = ( f + xF 1) ( g + xG 1) = f g + x( f G 1 + F 1 ∗ G)

Переведём:

(.*) :: Num a => a -> Ps a -> Ps a

k .* (f :+: fs) = (k * f) :+: (k .* fs)

(f :+: fs) * (g :+: gs) = (f * g) :+: (f .* gs + fs * (g :+: gs))

Дополнительная операция (.*) выполняет умножение всех коэффициентов ряда на число.

Класс Num

Соберём определения для методов класса Num вместе:

instance Num a => Num (Ps a) where

(f :+: fs) + (g :+: gs) = (f + g) :+: (fs + gs)

(f :+: fs) * (g :+: gs) = (f * g) :+: (f .* gs + fs * (g :+: gs))

negate (f :+: fs) = negate f :+: negate fs

fromInteger n = p0 (fromInteger n)

(.*) :: Num a => a -> Ps a -> Ps a

k .* (f :+: fs) = (k * f) :+: (k .* fs)

Методы abs и signum не определены для рядов. Обратите внимание на то, как рекурсивное определение

рядов приводит к рекурсивным определениям функций для рядов. Этот приём очень характерен для Haskell.

Поскольку наш ряд это число и ещё один ряд за счёт рекурсии мы можем воспользоваться операцией, которую

мы определяем, на “хвостовом” ряде.

Деление