(Haskell) Как конфертировать из Ratio Integer в Ratio Rational? - PullRequest
1 голос
/ 04 октября 2019

Как работает сложение двух разных соотношений? Например, целочисленное соотношение и рациональные соотношения, похоже, не добавляются. Я попытался оценить непрерывную дробь для данного списка.

Вот код:

import Data.Ratio
f :: [Integer] -> Rational
f(x:xs)
  | (null xs == True) = x
  | otherwise       = x + (1 % f xs)

Какой должна быть правильная версия кода? Поскольку f дает Ratio Rational, я чувствую, что x, если привести тип к рациональному числу Ratio, будет достаточно.

Ответы [ 2 ]

3 голосов
/ 04 октября 2019

Преобразование типов не требуется, используйте правильное деление между рациональными.

import Data.Ratio

f :: [Integer] -> Rational
f []           = error "empty list"
f [x]          = x % 1
f (x:xs@(_:_)) = x % 1 + 1 / f xs

Здесь x % 1 превращает x в Rational (хорошо, это преобразование, если хотите). Мы могли бы также использовать fromInteger, я думаю.

Тогда между Rational значениями мы не используем %, что приводит к странным Ratio Rational, но мы используем /, которое производитRational вместо.

2 голосов
/ 04 октября 2019

Преобразование x в Rational здесь будет недостаточно. Так как вы здесь пишите 1 % f xs. Тип (%) равен (%) :: Integral a => a -> a -> Ratio a, и поскольку f xs является Rational, а Rational не является экземпляром Integral, нам необходимочтобы исправить вторую проблему.

Однако это не так сложно. Например, мы можем создать функцию, которая вычисляет обратное значение:

inverseR :: Integral a => Ratio a -> Ratio a
inverseR r = denominator r % numerator r

Поскольку Ratio a является экземпляром Num, а a является экземпляром Integral, мы можем использовать fromInteger :: Num a => Integer -> a:

f :: [Integer] -> Rational
f [x] = fromInteger x
f (x:xs) = fromInteger x + inverseR (f xs)

Например:

Prelude Data.Ratio> f [1,4,2,5]
60 % 49

С 1 + 1 / (4 + 1 / (2 + 1/5))) = 1 + 1 / (4 + 1 / (11/5)) = 1 + 1 / (4 + 5/11) = 1 + 1 / (49/11) = 1 + 11/49 = 60/49.

Мы можем улучшить это следующим образом:

  1. , используя fromIntegral :: (Integral a, Num b) => a -> b для преобразования любого интеграла в Ratio
  2. с помощью (/) :: Fractional a => a -> a -> a.

Таким образом, мы можем обобщить это для функции:

f :: (Integral a, Fractional b) => [a] -> b
f [x] = fromIntegral x
f (x:xs) = fromIntegral x + 1 / f xs

Что дает одно и то же значение:

Prelude Data.Ratio> f [1,4,2,5] :: Rational
60 % 49

Мы можем использовать шаблон foldr и избежать явной рекурсии:

f :: (Integral a, Fractional b) => [a] -> b
f = foldr1 (\x -> (x +) . (1 /)) . map fromIntegral
...