본문 바로가기

Haskell

e^x를 계산하는 함수

\(e^x\)를 계산하기

\(e ^ x\)는 다음 식으로 계산된다.

$$ 1 + x + x^2/2! + x^3/3! ... $$

이계산을 10단계까지 확장한 값을 계산하는 프로그램을 작성하라

(https://www.hackerrank.com/challenges/eval-ex/problem)

여기서 헷갈리기 쉬운 것이 바로 Double 타입과 Int 타입간의 연산이다. 두 타입은 엄격히 다르며, 하스켈은 자동 형변환을 지원하지 않기 때문에 1.0 + 3 같은 표현식은 에러가 난다.

물론 이 식을 REPL에서 > 1.5 + 3 하면 4.5로 계산하는데 이는 REPL이 뒤의 3을 Double 타입으로 읽었기 때문이다. 타입을 명시해서 > 1.5 + (3::Int)라고 적시하는 경우에는 에러로 처리된다.

문제에서 주어진 식은 k=0,1,2,.... 일 때 $x^k/k!$를 계산하는 것이니, 간단한 함수를 하나 만들면 된다. 그전에 팩토리얼을 계산할 함수를 하나 작성하자.


fact :: Int -> Double
fact n
	| n < 2 = 1
	| otherwise = fromIntegral . foldr1 (*) [2..n]

두 번째는 power 연산 ( a의 b 승)을 구하는 것인데, 하스켈에서 이 연산은 오버로딩되어 있지 않고 다음과 같이 명시적으로 분리되어 있다. (https://wiki.haskell.org/Power_function)

  • (^) :: (Num a, Integral b) => a -> b -> a
  • (^^) :: (Fractional a, Integaral b) => a -> b -> a
  • (**) :: (Floating a) => a -> a -> a

밑이 실수인 경우에는 ^^를, 지수도 실수인 경우에는 **를 쓰는 것이 일반적이며 둘 다 정수일 때만 ^를 쓴다고 알아두자.

거듭제곱값과 팩토리얼 값을 나눠야 하기 때문에 fact 함수는 최종적으로 fromIntegral 함수를 이용해서 실수 값으로 변환해둔다.

이제 solve 함수를 작성해보자.


solve :: Double -> Double
solve x = sum [(x^^k) / (fact k) | k <- [0..9]

여기까지 진행했으면 solve 함수의 구현이 별로 어렵지는 않을 것이다.

최종 결과를 보자


import Control.Applicative
import Control.Monad
import System.IO

fact :: Int -> Double
fact n
	| n < 2 = 1
	| otherwise  = fromIntegral . foldr1 (*) $ [2..n]
	
solve :: Double -> Double
solve x = sum[ (x^^k) / (fact k) | k <- [0..9]]

main :: IO ()
main = do
	l <- readLn :: IO Int
	forM_ [1..n] $ \a0 -> do
		xtemp <- getLine
		let x = read xtemp :: Double
		putStrLn . show . solve $ x