52700.fb2
просто 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.
Поскольку наш ряд это число и ещё один ряд за счёт рекурсии мы можем воспользоваться операцией, которую
мы определяем, на “хвостовом” ряде.
Деление