Оригинал доступен на cs.dartmouth.edu
Задание значения
Задание значения преобразовывает скалярную величину, такую как 2 в степенной ряд [2,0,0, …]. Это позволяет нам писать выражения типа 2*sins – где sins является степенным рядом – не нарушая требования Haskell о том, что оба операнда * должны принадлежать к одному типу данных.
Haskell неявно применяет к целочисленным константам функцию задания значения frominteger. Мы придаем frominteger значение, которое показывает, как преобразовывать целые числа в степенные ряды.
frominteger принадлежит классу типов Num наряду с обычными арифметическими операциями «+», «-» и «*». Мы помещаем степенные ряды, представленные в виде списков чисел, в указанный класс с помощью объявления, которое, в частности, гласит:
instance (Num a, Eq a) => Num [a] где
fromInteger c = series(fromInteger c)
Это помещает тип [a] – то есть списки с элементами типа a — в класс Num при условии, что сам тип a находится в классе Num.
Новый экземпляр fromInteger типа Num a=>Integer−>[a]определяется с помощью старого экземпляра типа Num a=>Integer−>a. Это обеспечивает определение c в типе, необходимом для элементов списка, обычно это Integer или Rational.
Отрицание
Стандартное наименование для унарного отрицания в Haskell – это negate, но оно может быть записано в выражениях как «−».
Умножение
Если F и G – это два степенных ряда с первыми членами f и g и последними F’ и G’, таким образом, что
F(x) = f + xF’(x)
G(x) = g + xG’(x),
то тогда их произведение будет
fg + x(F’)(g + xG’) + xfG’
что непосредственно преобразуется в
(f:ft) * gs@(g:gt) = f*g : ft*gs + series(f)*gt
Поскольку сигнатура (*) равна Num a=>a−>a−>a, мы не можем использовать (*) для умножения данных разного типа; отсюда и задание значения ряду. Если допускаются конечные ряды – как в практичном пакете, – то fG’ может быть реализовано как [f]*gt.
Подсказка. Более эффективной – менее явной – реализацией fG’ является map (f*) gt.
Деление
Если Q является частным рядов F и G, то у нас получится:
F = f+xF’ = QG = (q+xQ‘)(g+xG’)
откуда видно, что f = qg и F’ = (q+xQ‘)G’ + Q‘g = QG’ + Q‘g, и, наконец, что
Q = q+xQ‘ = f /g + x(1/g)(F’ − QG’)
Длинное деление вкратце!
Сложная функция
Сложная функция F(x) и G(x) равна F(G(x)), которая расширяется до
f + (g+xG’)F’(G)
Первым параметром F(G) является f + gF'(g), что является бесконечной суммой, если только F не является конечным рядом (то есть многочленом). Таким образом, для сохранности нам требуется g = 0. Формула сложной функции становится такой:
F(G(x)) = f + xG’F’(G)
Реверсия
Реверсия – это процесс вычисления композиционного обратного степенного ряда F(x).
Обратное R удовлетворяет F(R(x)) = x, которое расширяется до
f + (r+xR‘)(F’(R)) = x
Чтобы обратная функция F'(R) выполнялась, у нас должно быть r = 0, из чего следует, что f = 0.
Решение для R’ дает
R‘ = 1/F’(R)
Интеграция и дифференциация
Использование последовательности [1..], в выражениях
int fs = 0 : zipWith (/) fs [1..]
diff (_:ft) = zipWith (*) ft [1..]
это хорошо, но не идеально, поскольку вводит класс Enum в контексты типа:
int :: (Fractional a, Enum a) => [a] −> [a]
diff :: (Num a, Enum a) => [a] −> [a]
Enum в типе напоминает нам, что эти операции объединяют коэффициенты с индексами терминов, которые задают определенные арифметические последовательности, тем самым накладывая ограничения на Enum. Reminder жесткий; int pascal становится ошибкой типа, потому что коэффициенты в pascal являются списками в классе Num, но не в классе Enum. Проблема может быть решена переброской степенных рядов в класс Enum (так же довольно непринужденно, как Haskell проделывает это с рациональными числами) или путем отказа от записи арифметической последовательности. Первоначально выполнив последнее, практичный пакет теперь идет альтернативным путем: сопоставьте fromInteger со списком [1..], чтобы получить желаемый тип: Num a => [a].
Примеры
Проверка соответствия типов в формуле
tans = revert(int(1/(1:0:1)))
показывает, что (1:0:1) должен быть списком. Поскольку правый операнд (:) является списком, то последняя «1» должна быть списком; поэтому ей задается значение ряда. Мы можем заменить (1:0:1) на [1,0,1] только когда используем пакет степенных рядов, подстроенный под обработку конечных списков.
Подсказка. Данное выражение рационально, но tans = sins/coss проще и быстрее.
В определении pascal
1/[1, −[1,1]]
представляет 1/(1z0 − (1x0+1x1)z1). Автоматическое задание значений преобразует его в неуклюжую форму:
[[1]]/[[1], −[1,1]]
Добавить комментарий