2010-08-04 2 views
5

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

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

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

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

Как один идиоматически обрабатывать интегральное -> Дробное/Реальное преобразование, необходимое в 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

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

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 
6
fromIntegral :: (Integral a, Num b) => a -> b 

r = fromIntegral i 
+0

Вопрос о fromIntegral. Я пытался это, но не в правильном месте на ответ Трэвиса, и я думал, что он не работает, потому что согласно этому http://www.haskell.org/tutorial/numbers.html num не предоставляет оператор разделения. Является ли fromIntegral полиморфным по типу возвращаемого значения и ghc указывает, что мне нужен тип возврата Fractional? – asm

+0

Да, 'fromIntegral' является полиморфным в своем обратном типе' b', в этом 'b' (возвращаемый тип) разрешен любой тип данных, который является членом класса типа« Num ». В то время как класс типа «Num» не предоставляет разделение, некоторые типы данных, которые являются членами этого типа, предоставляют деление, например «Двойной». – yfeldblum

1

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

if odd (round k) then 4 else 2 

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

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

sigma f a next b = sum $ map f $ takeWhile (<= b) $ iterate next a 
Смежные вопросы