태그 보관물: Haskell

(연습문제) 대소문자변환

연습문제 : 대소문자 변환하기

입력받은 문자열의 대소문자를 반전하여 출력하는 프로그램을 작성하시오.

입력받은 각 글자의 문자 코드가 ‘a’ … ‘z’ 사이에 있으면 소문자, ‘A’~’Z’ 사이에 있으면 대문자이다. 그리고 그 변환은 해당 코드값에 a - A를 더하거나 빼주면 된다.

먼저 대소문자의 범위를 찾아보자.

print("azAZ".utf8.map(Int.init))
//=> [97, 122, 65, 90]

즉 문자 코드가 97~122 구간에 있으면 소문자, 65~90 구간에 있으면 대문자이다. 소문자->대문자 변환은 32를 빼고, 반대의 변환은 32를 더한다.

문자 코드 값으로 다시 문자를 만드는 방법은 다음과 같다.

  1. 문자열은 [Character] 타입으로 만들 수 있다.
  2. 코드값으로 Character 인스턴스를 만들기 위해서는 Int 값을 UnicodeScalar 값으로 변환하면 된다.

즉, 다음과 같이 변경할 수 있다.

let arr = [97, 122, 65, 90]
let str = String(arr.map{ Character(UnicodeScalar($0)) })
print(str)
//=> "azAZ"

전체 코드는 다음과 같다.

if let s = readLine(), case let u = s.utf8.map(Int.init) {
    let h: Int -> Int = { n in
        switch n {
        case 97...122: return n - 32
        case 65...96: return n + 32
        default: return n
        }
    }
    let result = String(u.map{ Character(UnicodeScalar(h($0))) })
    print(result)
}

위의 switch 문은 간단히 삼항 연산자의 중첩으로 쓸 수 있다. 범위(Range<Int>)에 특정 값이 있는지는 패턴매칭으로 찾는데, 패턴매칭은 연산자 ~=를 쓰므로 다음과 같이 코드를 줄일 수 있다.

/* shorter version */
if let s = readLine(), case let u = s.utf8.map(Int.init) {
    let h: Int -> Int = { n in return 97...122 ~= n ? n - 32 : 65...96 ~= n ? n + 32 : n }
    let result = String(u.map{ Character(UnicodeScalar(h($0))) })
    print(result)
}

하스켈 풀이

하스켈의 경우, Data.Char 모듈에 isUpper, toUpper, toLower 함수가 정의되어 있으므로 이를 이용할 수 있다.

import Data.Char
main = interact (map f) where f c = if isUpper c then toLower c else toUpperc

interact 함수는 String -> String -> IO () 타입의 함수로 입력받은 문자열을 처리해주는 함수를 받아서 입력과 출력을 연결해주는 함수이다. 즉,

main = getLine >= (\xs -> putStrLn . (map f) $ xs)

를 줄인 것이다.

하스켈에서 조합 구현하기

하스켈 연습문제

주어진 리스트에서 중복되지 않은 모든 부분집합을 만들어라.

주어진 리스트에서 n 개를 비복원추출한 조합은 다음과 같이 만든다.

combinations :: Int -> [a] -> [[a]]
combinations 0 _ = [[]]
combinations n xs = [ xs !! i : x | i <- [0..(length xs)-1],
                                    x <- combinations (n-1) (drop (i+1) xs)]

> combinations 2 [1..4]
[[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]

n 개짜리 원소의 리스트의 모든 부분집합은 따라서 다음과 같이 구한다.

combinationAll xs = concatMap (\n -> combinations n xs) [0..length xs]

tails 함수를 이용하기

tails 함수는 앞에서부터 원소를 하나씩 제거한 부분집합을 만들어 낸다.

--   >:m Data.List
--   > tails [0,1,2,3]
--   [[0,1,2,3],[1,2,3],[2,3],[3],[]]

이를 이용해서 다음과 같이 정의할 수 있다.

import Data.List (tails)
combinationsAll xs = concatMap (\n -> combinations n xs) [0..length xs]
where
    combinations 0 _ = [[]]
    combinations n xs = [ y:ys | (y:xs') <- tails xs, ys <- combinations (n-1) xs']