(연습문제) 행렬의 대각선의 차
대각선의 차이 구하기
N*N 크기의 행렬이 주어졌을 때, 이 행렬의 대각선상에 위치하는 요소들의 합끼리의 차를 구하는 프로그램을 작성하는 문제이다.
i 번째 행의 (i-1)번, 및 (N-i)번 인덱스의 값을 각각 뺀 다음 (i=1,2,3… 인덱스는 0, 1, 2 순) 이들을 합산하고 절대값을 취하면 된다.
module Main where
import Control.Monad
import Control.Applicative
-- i 와 i번 행을 받아 이를 처리한다.
processLine :: Int -> [Int] -> Int
process i xs = let a = head $ drop (i-1) xs
b = head . drop (i-1) . reverse $ xs
in a - b
main = do
n <- readLn :: IO Int
ts <- sum <$> forM [1..n] (\x -> processLine x . map read . words <$> getLine)
print . abs $ ts
여기서도 <$>
연산자를 유용하게 썼고, forM
함수도 썼다.
forM
, <$>
forM
함수는 (Monad m, Traversable t) => t a -> (a -> m b) -> m (t b)
의 타입을 가진 함수로 리스트나 트리와 같은 순회 가능한 목록과 모나딕 값을 리턴하는 함수를 이용해서 리스트의 각 원소를 처리하고, 이를 모나딕 리스트나 모나딕 트리로 리턴하는 함수이다.
여기서 두 번째 인자로 쓰인 함수를 살펴보면
(\x -> processLine x . map read . words <$> getLine)
이게 나오는데 <$>
는 좌변의 함수(a -> b)를 우변의 t a
에 적용하여 t b
타입을 만들어주는 (여기서 t 는 applicative) 연산자이다. 즉 좌변의 함수를 Applicative
문맥으로 감싸고 이를 다시 Applicative
문맥에 들어있는 값에 적용하게 해준다. (인자가 2개 이상이면 <*>
연산자를 이용해서 언커리할 수 있다.)
여기서 왼쪽의 함수는 String -> Int
가 되고, 우변의 getLine
은 IO String
이므로 이 함수 전체의 타입은 Int -> IO Int
가 된다.
결국 forM
함수를 이용해서 처리한 결과는 IO [Int]
가 되고 이는 (<$>) sum
에 의해서 IO Int
로 귀결된다.
원본
기본으로 주어지는 템플릿은 다음과 같은 식으로 표현하고 있다. (processLine은 제외) IO 와 관련하여 입력받은 값을 처리하는데 있어서 Applicative
의 활용이 얼마나 중요한지를 보여주는 좋은 예라 하겠다.
import Control.Applicative
import Control.Monad
import System.IO
main :: IO ()
main = do
n_temp <- getLine
let n = read n_temp :: Int -- 이 두 라인은 n <- readLn :: IO Int 로 대체할 수 있다.
a_temp <- getMultipleLines n
let a = map ( map ( read :: String -> Int ) . words ) a_temp
getMultipleLines :: Int -> IO [String]
getMultipleLines n
| n <= 0 = return []
| otherwise = do
x <- getLine
xs <- getMultipleLines (n-1)
let ret = (x:xs)
return ret