Иерархия числовых типов в Haskell в упражнениях SICP - PullRequest
5 голосов
/ 04 августа 2010

Я недавно изучал Haskell и разговаривал с другом, который работает через SICP.Нам было любопытно сравнить Common Lisp и Scheme, и поэтому я решил в качестве упражнения попытаться перевести упражнение 1.29 в Haskell.

В этом упражнении используется функция сигма, которая представляет математическую функцию суммирования сигма.Эта функция принимает функцию f, применяемую к каждому члену, нижнюю границу, функцию, применяемую к каждому члену, чтобы получить следующий член, и верхнюю границу.Он возвращает сумму f, примененную к каждому члену.

simpsonIntegral должен использовать правило Симпсона для аппроксимации интеграла функции f в диапазоне [a, b] с использованием «точности» n.У меня проблемы с тем, чтобы заставить эту функцию работать, потому что, кажется, есть что-то, чего я не понимаю в задействованных типах.

Этот код будет компилироваться с версией 6.12.1 ghc, но simpsonIntegral получит контекст типа (Integral a, Fractional a), который не имеет никакого смысла, и функция взрывается, как только вы ее вызываете,В какой-то момент у меня это сработало, но то, что я сделал, было настолько очевидно хаком, что я хотел спросить здесь, как это будет обрабатываться идиоматически.

Как идиоматически обрабатывать Integral -> Fractional / Real преобразование, необходимое в h?Я прочитал несколько вещей, но ничто не казалось очевидным и чистым.

sigma :: (Ord a, Num b) => (a -> b) -> a -> (a -> a) -> a -> b
sigma f a next b = iter a 0
  where
    iter current acc | current > b = acc
                     | otherwise = iter (next current) (acc + f current)

simpsonIntegral f a b n = 1.0 * (h / 3) * (sigma simTerm 0 (1+) n)
  where
    h = (b - a) / n
    simTerm k = (yk k) * term
      where
        yk k = f (a + h * k)
        term =
          case k of
            0 -> 1
            1 -> 1
            otherwise -> if odd k then 4 else 2

Ответы [ 3 ]

6 голосов
/ 04 августа 2010
fromIntegral :: (Integral a, Num b) => a -> b

r = fromIntegral i
3 голосов
/ 05 августа 2010

Чтобы проконтролировать ответ юстиции: если вам интересно, куда поместить fromIntegral s, следующие компиляции:

simpsonIntegral :: (Integral a, Fractional b) => (b -> b) -> a -> a -> a -> b
simpsonIntegral f a b n = 1.0 * (h / 3) * (sigma simTerm 0 (1+) n)
  where
    h = fromIntegral (b - a) / fromIntegral n
    simTerm k = (yk k) * term
      where
        yk k = f (fromIntegral a + h * fromIntegral k)
        term = 
          case k of
            0 -> 1
            1 -> 1
            otherwise -> if odd k then 4 else 2

И, кажется, работает:

*Main> simpsonIntegral (^3) 0 1 100
0.2533333233333334
*Main> simpsonIntegral (^3) 0 1 1000
0.2503333333323334
1 голос
/ 07 августа 2010

Проблема в том, что функция "нечетная" ожидает, что ее аргумент имеет тип Integral. Затем компилятор делает вывод, что ваша переменная "k" имеет тип Integral. Но с помощью операции "/" компилятор выводит, что "k" также имеет тип Fractional. Решение может быть таким же простым, как преобразование «k» в целое число, где это действительно необходимо:

if odd (round k) then 4 else 2

Если вы хотите узнать больше о преобразовании чисел в Haskell, отметьте Converting_numbers

В качестве дополнительного примечания, вот еще один способ написания вашей сигма-функции:

sigma f a next b = sum $ map f $ takeWhile (<= b) $ iterate next a
...