일.참조
모듈을 참조하는 구문 형식은 다음과 같습니다:
-- 모듈의 모든 함수를 전역 네임스페이스에 추가
import <module>
-- 일부 참조
import <module> (fn1, fn2)
-- 데이터 타입과 그 값 생성자를 참조
import <module> (Datatype(constructor, constructor))
-- 모든 값 생성자를 참조
import <module> (Datatype(..))
-- hiding 으로 제외
import <module> hiding (fn)
-- 네임스페이스 유지
import qualified <module>
-- 네임스페이스 유지 및 별명 지정
import qualified <module> as <alias>
예를 들어:
import Data.List
import Data.List (nub, sort)
import Data.Tree (Tree(Node, Branch))
import Data.Tree (Tree(..))
import Data.List hiding (nub)
import qualified Data.Map
import qualified Data.Map as M
hiding 구문은 명명 충돌 문제를 완화할 수 있지만 매우 편리한 것은 아닙니다. 대량의 명명 충돌이 있는 모듈의 경우 qualified 로 네임스페이스를 유지하여 충돌을 피할 수 있습니다
GHCi 환경
:m 명령으로 모듈 참조:
> :m Data.List
> :m Data.List Data.Map Data.Set
GHC 7.0 이후, GHCi 환경에서 직접 import 구문을 사용할 수 있습니다:
> import qualified Data.Map as M
> M.fromList [('a', 1)]
fromList [('a',1)]
따라서 환경 차이를 신경 쓸 필요가 없습니다. 자세한 내용은 import qualified in GHCI 참조
이.선언
모듈은 코드를 구성하는 데 사용됩니다. 예를 들어 기능이 유사한 함수를 같은 모듈에 넣습니다
예를 들어 이진 트리의 모듈 정의:
module BTree
-- 공개할 함수와 데이터 타입 선언
( Tree
, singleton
, add
, fromList
, find
) where
-- 의존 모듈 참조
-- 데이터 타입과 함수 정의
data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)
singleton x = Node x EmptyTree EmptyTree
주의:
-
모듈 이름과 파일 이름을 동일하게 해야 하므로, 해당하는 파일 이름은
BTree.hs여야 합니다 -
모듈 선언은 첫 번째 행에 위치해야 합니다 (이전에
import같은 것을 넣을 수 없으며,import는where뒤에 넣을 수 있습니다)
모듈 내 데이터 구조의 내보내기는 import 구문과 유사합니다:
module MyModule (Tree(Branch, Leaf)) where
data Tree a = Branch {left, right :: Tree a} | Leaf a
데이터 구조 Tree 와 그 생성자 Branch 와 Leaf 만 공개합니다. .. 를 사용하여 모든 값 생성자를 공개할 수도 있습니다:
module MyModule (Tree(..))
또는 값 생성자를 공개하지 않고 팩토리 메서드 등의 방식으로만 해당 타입의 값을 얻을 수 있도록 합니다 (일반적으로 Map.fromList 등):
module MyModule (Tree, factory)
단점은 값 생성자를 사용하여 패턴 매칭을 할 수 없게 된다는 것입니다
서브모듈
모듈에는 트리 계층 구조가 있습니다. 모듈은 서브모듈을 가질 수 있으며, 서브모듈은 다시 서브모듈을 가질 수 있습니다……
디렉토리 구조와 명명에 요구 사항이 있습니다. 예를 들어:
.
├── main.hs
└── Math
├── Number.hs
└── Vector.hs
패키지 이름은 대문자로 시작해야 합니다 (Math). 서브모듈 파일 이름은 서브모듈 이름과 일치해야 합니다. 대소문자 구분은 환경에 따라 다릅니다 (예: OSX 는 구분하지 않음)
삼.표준 라이브러리 모듈
표준 라이브러리에는 많은 강력한 함수가 내장되어 있습니다. Hoogle 에서 사용 예시, 타입 선언, 심지어 소스 코드까지 확인할 수 있어 매우 편리합니다
Data.List
대량의 List 조작 함수를 제공합니다. 일반적인 것으로 map, filter 등이 있으며, 더 있습니다:
술어:
-- every, 모두 True 일 경우에만 True
and :: Foldable t => t Bool -> Bool
-- some, 하나라도 True 면 True
or :: Foldable t => t Bool -> Bool
-- 일반적인 some, List 내 임의의 요소가 조건을 만족하면 True
any :: Foldable t => (a -> Bool) -> t a -> Bool
-- 일반적인 every, List 내 모든 요소가 조건을 만족해야 True
all :: Foldable t => (a -> Bool) -> t a -> Bool
새 List 구축:
-- 배열 내에 구분 요소 삽입
intersperse :: a -> [a] -> [a]
-- intersperse 와 유사, 2 차원 배열 내에 1 차원 배열을 구분 요소로 삽입한 후 1 차원으로 평탄화
intercalate :: [a] -> [[a]] -> [a]
-- 2 차원 배열의 행렬 전치
transpose :: [[a]] -> [[a]]
-- 차원 축소 (한 그룹의 List 를 하나의 List 로 연결)
concat :: Foldable t => t [a] -> [a]
-- 매핑 후 차원 축소, concat . map 과 동등
concatMap :: Foldable t => (a -> [b]) -> t a -> [b]
-- 무한 재귀 호출, 반환 값을 다시 입력
iterate :: (a -> a) -> a -> [a]
-- 위치로 분할, 분할된 두 부분 반환
splitAt :: Int -> [a] -> ([a], [a])
-- 요소 취득, 조건을 만족하지 않을 때까지
takeWhile :: (a -> Bool) -> [a] -> [a]
-- 요소 삭제, 조건을 만족하지 않을 때까지
dropWhile :: (a -> Bool) -> [a] -> [a]
-- 조건으로 분할 (처음으로 조건을 만족하지 않는 위치), takeWhile 과 유사
span :: (a -> Bool) -> [a] -> ([a], [a])
-- 조건으로 분할 (처음으로 조건을 만족하는 위치)
break :: (a -> Bool) -> [a] -> ([a], [a])
-- 재귀 init, List 가 빌 때까지
inits :: [a] -> [[a]]
-- 재귀 tail, List 가 빌 때까지
tails :: [a] -> [[a]]
정렬:
-- 병합 정렬
sort :: Ord a => [a] -> [a]
-- List 내 해당 요소 이상인 첫 번째 요소 앞에 삽입
insert :: Ord a => a -> [a] -> [a]
그룹화:
-- 그룹화, 인접하고 값이 같은 것을 기준으로 함
group :: Eq a => [a] -> [[a]]
-- 조건으로 그룹화, 조건을 만족하는 한 그룹, 만족하지 않는 한 그룹
partition :: (a -> Bool) -> [a] -> ([a], [a])
매칭:
-- 부분 문자열 매칭 (부분 List 매칭), 지정 부분 문자열 포함 여부
isInfixOf :: Eq a => [a] -> [a] -> Bool
-- 부분 문자열 매칭, 지정 부분 문자열로 시작하는지 여부
isPrefixOf :: Eq a => [a] -> [a] -> Bool
-- 부분 문자열 매칭, 지정 부분 문자열로 끝나는지 여부
isSuffixOf :: Eq a => [a] -> [a] -> Bool
-- 요소 포함성 검출, 지정 요소 포함 여부
elem :: (Foldable t, Eq a) => a -> t a -> Bool
-- 요소 포함성 검출, 지정 요소 포함하지 않는지 여부
notElem :: (Foldable t, Eq a) => a -> t a -> Bool
검색:
-- 조건으로 검색, 조건을 만족하는 첫 번째 요소 반환
find :: Foldable t => (a -> Bool) -> t a -> Maybe a
-- 검색, 첫 번째 매칭 요소 인덱스 또는 Nothing 반환
elemIndex :: Eq a => a -> [a] -> Maybe Int
-- 모두 검색
elemIndices :: Eq a => a -> [a] -> [Int]
-- find 와 유사하지만 조건을 만족하는 첫 번째 요소의 인덱스 반환
findIndex :: (a -> Bool) -> [a] -> Maybe Int
-- find 와 유사하지만 조건을 만족하는 모든 항의 인덱스 반환
findIndices :: (a -> Bool) -> [a] -> [Int]
결합:
-- List 결합, zip3 ~ zip7 도 있음
zip :: [a] -> [b] -> [(a, b)]
-- List 결합 후 map一遍, zipWith3 ~ zipWith7 도 있음
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
텍스트 처리:
-- 문자열을 행으로 분할 (\n)
lines :: String -> [String]
-- 개행으로 결합 (\n)
unlines :: [String] -> String
-- 공백 문자로 분할
words :: String -> [String]
-- 공백으로 결합
unwords :: [String] -> String
요소 삭제:
-- 중복 제거
nub :: Eq a => [a] -> [a]
-- 첫 번째 매칭 요소 삭제
delete :: Eq a => a -> [a] -> [a]
집합 연산:
-- 차집합 구하기, 중복 요소가 있으면 첫 번째만 삭제
(\\) :: Eq a => [a] -> [a] -> [a]
-- 합집합 구하기
union :: Eq a => [a] -> [a] -> [a]
-- 교집합 구하기
intersect :: Eq a => [a] -> [a] -> [a]
더 일반적인 버전
length, take, drop, splitAt, !!, replicate 등 함수의 파라미터 또는 반환 값은 Int 타입이 요구되며 충분히 일반적이지 않아 더 일반적인 타입의 대응 버전이 제공되었습니다:
genericLength :: Num i => [a] -> i
genericTake :: Integral i => i -> [a] -> [a]
genericDrop :: Integral i => i -> [a] -> [a]
genericSplitAt :: Integral i => i -> [a] -> ([a], [a])
genericIndex :: Integral i => [a] -> i -> a
genericReplicate :: Integral i => i -> a -> [a]
nub, delete, union, intsect, group, sort, insert, maximum, minimum 은 모두 == 로 같은지 판단하며, 더 일반적이고 스스로 같은지 판단할 수 있는 버전도 제공됩니다:
nubBy :: (a -> a -> Bool) -> [a] -> [a]
deleteBy :: (a -> a -> Bool) -> a -> [a] -> [a]
unionBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
intersectBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
sortBy :: (a -> a -> Ordering) -> [a] -> [a]
insertBy :: (a -> a -> Ordering) -> a -> [a] -> [a]
maximumBy :: Foldable t => (a -> a -> Ordering) -> t a -> a
minimumBy :: Foldable t => (a -> a -> Ordering) -> t a -> a
By 가 있는 함수는 일반적으로 Data.Function.on 과 함께 사용됩니다:
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
f `on` g = \x y -> f (g x) (g y)
예를 들어 양수로 인접 요소를 그룹화하려면:
groupBy ((==) `Data.Function.on` (> 0)) values
의미가 매우 명확합니다: 요소가 0 보다 큰지에 따라 분류합니다
또한 sort 와 sortBy compare 는 동등합니다 (기본 비교 방식은 compare 입니다). List 길이로 정렬하려면 이렇게 합니다:
sortBy (compare `on` length) xs
의미도 마찬가지로 매우 명확합니다. 따라서
(==) `on`
compare `on`
은 매우 훌륭한 관용적 패턴입니다
P.S.:browse <module> 명령으로 모듈의 모든 함수와 데이터 타입의 타입 선언을 확인할 수 있습니다
Data.Char
String 은 실제로 [Char] 입니다:
type String = [Char] -- Defined in 'GHC.Base'
따라서 문자열을 처리할 때 자주 Data.Char 모듈을 사용하며, 많은 문자 관련 함수를 제공합니다
문자 범위 판정:
-- 제어 문자
isControl :: Char -> Bool
-- 공백 문자
isSpace :: Char -> Bool
-- 소문자 Unicode 문자
isLower :: Char -> Bool
-- 대문자 Unicode 문자
isUpper :: Char -> Bool
-- 문자
isAlpha :: Char -> Bool
-- 문자 또는 숫자
isAlphaNum :: Char -> Bool
-- 인쇄 가능 문자
isPrint :: Char -> Bool
-- ASCII 숫자, 0-9
isDigit :: Char -> Bool
-- 8 진수
isOctDigit :: Char -> Bool
-- 16 진수
isHexDigit :: Char -> Bool
-- 문자, 기능은 isAlpha 와 동등, 구현 방식이 다름
isLetter :: Char -> Bool
-- Unicode 발음 기호, 예: 프랑스어
isMark :: Char -> Bool
-- Unicode 숫자, 로마 숫자 등 포함
isNumber :: Char -> Bool
-- 구두점 기호
isPunctuation :: Char -> Bool
-- 통화 기호
isSymbol :: Char -> Bool
-- Unicode 공백 또는 구분 기호
isSeparator :: Char -> Bool
-- ASCII 문자 (Unicode 문자표의 처음 128 위)
isAscii :: Char -> Bool
-- Unicode 문자표의 처음 256 위
isLatin1 :: Char -> Bool
-- 대문자 ASCII 문자
isAsciiUpper :: Char -> Bool
-- 소문자 ASCII 문자
isAsciiLower :: Char -> Bool
소속 타입 판정:
generalCategory :: Char -> GeneralCategory
반환되는 GeneralCategory 는 열거형으로, 총 30 개 범주 가 있습니다. 예를 들어:
> generalCategory 'a'
LowercaseLetter
> generalCategory ' '
Space
문자 변환:
-- 대문자로 변환
toUpper :: Char -> Char
-- 소문자로 변환
toLower :: Char -> Char
-- title 형식으로 변환, toUpper 와 유사, 일부 연체 문자는 차이 있음
toTitle :: Char -> Char
-- 문자를 숫자로 변환, [0-9,a-f,A-F] 요구
digitToInt :: Char -> Int
-- 숫자를 문자로 변환
intToDigit :: Int -> Char
-- 문자를 Unicode 코드로 변환
ord :: Char -> Int
-- Unicode 코드를 문자로 변환
chr :: Int -> Char
따라서 간단한 암호화와 복호화는 이렇게 구현할 수 있습니다:
encode shift = map $ chr . (+ shift) . ord
decode shift = map $ chr . (subtract shift) . ord
-- 또는 더 기교적인
decode shift = encode $ negate shift
Data.Map
사전은 키 - 값 쌍의 순서 없는 목록으로, 균형 이진 트리 형태로 저장됩니다. Data.Map 은 일부 사전 처리 함수를 제공합니다
P.S.Data.Map 의 일부 함수는 Prelude 와 Data.List 모듈과 명명 충돌이 있으므로 qualified import as 를 사용하여 네임스페이스를 유지하고 별명을 지정합니다:
import qualified Data.Map as Map
새 Map 구축:
-- List 를 Map 으로 변환, 중복 key 가 있으면 마지막 value 취득
Map.fromList :: Ord k => [(k, a)] -> Map.Map k a
-- Map 을 List 로 변환
Map.toList :: Map.Map k a -> [(k, a)]
-- fromList 와 유사, 중복 key 를 직접 폐기하지 않고 수동으로 처리 가능
Map.fromListWith :: Ord k => (a -> a -> a) -> [(k, a)] -> Map.Map k a
-- 빈 Map
Map.empty :: Map.Map k a
-- (k, a) 삽입, 새 Map 반환
Map.insert :: Ord k => k -> a -> Map.Map k a -> Map.Map k a
-- insert 와 유사, 중복 key 처리 가능
Map.insertWith :: Ord k => (a -> a -> a) -> k -> a -> Map.Map k a -> Map.Map k a
-- 단일 요소 Map
Map.singleton :: k -> a -> Map.Map k a
-- 새 Map 으로 매핑
Map.map :: (a -> b) -> Map.Map k a -> Map.Map k b
-- 새 Map 필터
Map.filter :: (a -> Bool) -> Map.Map k a -> Map.Map k a
Map 정보 취득:
-- 빈지 판정
Map.null :: Map.Map k a -> Bool
-- 길이
Map.size :: Map.Map k a -> Int
-- 모든 key 취득
Map.keys :: Map.Map k a -> [k]
-- 모든 value 취득
Map.elems :: Map.Map k a -> [a]
검색:
-- key 로 검색
Map.lookup :: Ord k => k -> Map.Map k a -> Maybe a
-- 포함성 판정
Map.member :: Ord k => k -> Map.Map k a -> Bool
Data.Set
집합 관련 도구 함수를 제공하며, 구조는 Map 과 유사하며 모두 트리 구조로 저장됩니다
P.S.마찬가지로 대량의 명명 충돌이 있으므로 qualified import 가 필요합니다:
import qualified Data.Set as Set
집합 구축:
-- List 를 Set 으로 변환
Set.fromList :: Ord a => [a] -> Set.Set a
집합 조작:
-- 교집합 구하기
Set.intersection :: Ord a => Set.Set a -> Set.Set a -> Set.Set a
-- 차집합 구하기
Set.difference :: Ord a => Set.Set a -> Set.Set a -> Set.Set a
-- 합집합 구하기
Set.union :: Ord a => Set.Set a -> Set.Set a -> Set.Set a
-- 부분집합인지 판정
Set.isSubsetOf :: Ord a => Set.Set a -> Set.Set a -> Bool
-- 진부분집합인지 판정
Set.isProperSubsetOf :: Ord a => Set.Set a -> Set.Set a -> Bool
주의, 함수 이름이 장난꾸러기네요, 배열의 List.intersect 는 집합이 되면 Set.intersection 으로 변합니다
Map 의 많은 함수가 Set 내에도 대응 버전이 있습니다. 예를 들어 null, size, member, empty, singleton, insert, delete, map, filter 등
마찬가지로 집합을 사용하여 한 줄로 중복 제거를 구현할 수 있습니다:
unique :: Ord a => [a] -> [a]
unique = Set.toList . Set.fromList
집합 중복 제거 효율은 List.nub 보다 높지만, 단점은 집합을 구축할 때 요소를 정렬하므로 얻어지는 중복 제거 결과는 원래 순서를 유지하지 않습니다 (List.nub 은 유지합니다)
참고 자료
-
Haskell data type pattern matching:커스텀 데이터 타입 패턴 매칭
아직 댓글이 없습니다