Graphics Programming
[ALGOSPOT] ENDIANS - IO 액션 직접 만들어보기 본문
1. 정수 N을 입력받는다.
2. N개의 32비트 정수를 입력받아 반대 엔디안으로 바꿔서 출력한다.
워낙 간단한 문제니 전략을 바로 세울 수 있다. 문제 하나를 푸는 액션 solve를 정의한다. N을 입력받아서 solve를 N번 실행한다. 하스켈 코드로 그 개요를 표현하자면 다음과 같다.
import Control.Monad
-- replicateM_은 여러 액션을 실행하고 결과는 버린다 (액션들을 >> 연산자로 연쇄하는 것과 같은 효과)
main = 입력 >>= (\ 문제수 -> replicateM_ 문제수 solve)
문제는 입력이 4 123 3166 4232 874 처럼 띄어쓰기 형태로 들어올 경우 어떻게 하냐는 것인데.. 실제로는 줄바꿈(line break)로 나눠져서 들어온다 쳐도 궁금하니까 띄어쓰기를 가정하고 풀어보기로 했다. 그럼 입력 함수를 찾아볼까...
했는데 getLine :: IO String과 getChar :: IO Char 밖에 못 찾았다. 다행히 입력이 무조건 32비트 양의 정수이므로 getChar를 사용하면 구현할 수 있을 것 같다. 입력이 '0'~'9' 중 하나면 계속 덧붙이고 아니면 끝내는 것이다. getInt :: IO Int 같은 게 라이브러리에 있든 없든 구현해보면 공부는 되겠지 --
getInt :: IO Int
getInt = getChar >>= (\ c -> if ('0' <= c && c <= '9') then ??? else ???)
then에 뭘 넣어야 하지? getInt를 다시 호출하면 되나?
-- Data.Char 모듈의 digitToInt 함수는 '0' ~ '9' 사이의 문자를 대응하는 숫자로 변환한다
getInt :: IO Int
getInt n = getChar >>= (\ c -> if ('0' <= c && c <= '9') then (getInt n*10 + digitToInt c) else ???)
아니다. 뭔가 이상하다. 우리는 getLine이나 getChar를 호출할 때 인자를 주지 않는다. 이렇게 하면 getInt를 실행할 때 getInt 0이라고 써야 한다.
getInt :: IO Int
getInt = sub 0
where getChar >>= (\ c -> if ('0' <= c && c <= '9') then sub (n*10 + digitToInt c) else ???)
이제 재귀 부분은 끝났고 else에서는 실제로 IO Int로 평가되며 이 액션이 끝나야 한다. IO n이라고 쓰면 되나..? 아니다. Monad 클래스의 선언을 생각해보니 return 함수가 있었다. return의 타입은 return :: a -> m a다.
getInt :: IO Int
getInt = sub 0
where getChar >>= (\ c -> if ('0' <= c && c <= '9') then sub (n*10 + digitToInt c) else return n)
그런데 이게 정말 작동은 할까? 함수를 정의하는 거야 당연히 잘 될 것 같은데 액션을 정의하려니 조금 생소하다. 모르겠다 그냥 타입은 맞으니까 된다고 생각해야지 --
-- getInt 실험
main = getInt >>= (\ num -> print num)
되..된다!
이제 getInt를 이용해서 맨 처음 코드를 진행할 수 있다.
import Control.Monad
main = getInt >>= (\ numProbs -> replicateM_ numProbs solve)
solve = getInt >>= (\ num -> print (convert num))
-- 엔디안 변환하는 함수
convert :: Int -> Int
convert = ???
이제 convert 함수에서 비트 조작을 해야 하는데 찾아보니 Data.Bits 모듈에 타 언어에도 으레 있는 비트 조작 함수들이 있다. 특수 기호를 다른 종류의 연산자들에 너무 많이 써서 그런지 비트 조작 연산자들은 죄다 이상한 기호거나 아예 일반 함수를 중위로 써야 한다. (논리곱은 .&. 이고 우측 쉬프트는 `shiftR` 등) 어쨌든 비트 조작은 중요한 게 아니니까 이리저리 끼워맞추고 최종 코드를 정리하면
끝