본문으로 건너뛰기

모듈_Haskell 노트 2

무료2018-04-26#Functional_Programming#Haskell模块#Haskell module syntax#Haskell模块定义

모듈의 선언, 참조 방식, 및 일반적인 표준 라이브러리 함수 개요

일.참조

모듈을 참조하는 구문 형식은 다음과 같습니다:

-- 모듈의 모든 함수를 전역 네임스페이스에 추가
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 같은 것을 넣을 수 없으며, importwhere 뒤에 넣을 수 있습니다)

모듈 내 데이터 구조의 내보내기는 import 구문과 유사합니다:

module MyModule (Tree(Branch, Leaf)) where

data Tree a = Branch {left, right :: Tree a} | Leaf a

데이터 구조 Tree 와 그 생성자 BranchLeaf 만 공개합니다. .. 를 사용하여 모든 값 생성자를 공개할 수도 있습니다:

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 보다 큰지에 따라 분류합니다

또한 sortsortBy 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 의 일부 함수는 PreludeData.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 은 유지합니다)

참고 자료

댓글

아직 댓글이 없습니다

댓글 작성