Data.List
Data.List
Data.List 모듈은 리스트 조작에 필요한 기본 함수들을 제공해요.
본문
20.1 기본 함수 (Basic functions)
(++) :: [a] -> [a] -> [a]
두 리스트를 이어붙여요. 즉,
[x1, ..., xm] ++ [y1, ..., yn] == [x1, ..., xm, y1, ..., yn]
[x1, ..., xm] ++ [y1, ...] == [x1, ..., xm, y1, ...]
첫 번째 리스트가 유한하지 않으면 결괏값은 첫 번째 리스트예요.
head :: [a] -> a
리스트의 첫 번째 요소를 추출해요. 리스트는 비어 있지 않아야 해요.
last :: [a] -> a
리스트의 마지막 요소를 추출해요. 리스트는 유한하고 비어 있지 않아야 해요.
tail :: [a] -> [a]
리스트에서 head 뒤의 요소들을 추출해요. 리스트는 비어 있지 않아야 해요.
init :: [a] -> [a]
리스트에서 마지막 요소를 뺀 나머지 모든 요소를 돌려줘요. 리스트는 비어 있지 않아야 해요.
null :: [a] -> Bool
리스트가 비어 있는지 검사해요.
length :: [a] -> Int
O(n). length는 유한 리스트의 길이를 Int로 돌려줘요. 이는 결괏값 타입이 어떤 숫자 종류든 될 수 있는 더 일반적인 Data.List.genericLength의 한 인스턴스예요.
20.2 리스트 변환 (List transformations)
map :: (a -> b) -> [a] -> [b]
map f xs는 f를 xs의 각 요소에 적용해 얻은 리스트예요. 즉,
map f [x1, x2, ..., xn] == [f x1, f x2, ..., f xn]
map f [x1, x2, ...] == [f x1, f x2, ...]
reverse :: [a] -> [a]
reverse xs는 xs의 요소들을 역순으로 돌려줘요. xs는 유한해야 해요.
intersperse :: a -> [a] -> [a]
intersperse 함수는 요소와 리스트를 받아 리스트의 요소들 사이에 그 요소를 '사이사이에 넣어요(intersperses)'. 예를 들어,
intersperse ',' "abcde" == "a,b,c,d,e"
intercalate :: [a] -> [[a]] -> [a]
intercalate xs xss는 (concat (intersperse xs xss))와 동등해요. 리스트 xs를 xss의 리스트들 사이에 끼워 넣고 결과를 이어붙여요.
transpose :: [[a]] -> [[a]]
transpose 함수는 인자의 행과 열을 바꿔요. 예를 들어,
transpose [[1,2,3],[4,5,6]] == [[1,4],[2,5],[3,6]]
subsequences :: [a] -> [[a]]
subsequences 함수는 인자의 모든 부분 수열(subsequence)의 리스트를 돌려줘요.
subsequences "abc" == ["","a","b","ab","c","ac","bc","abc"]
permutations :: [a] -> [[a]]
permutations 함수는 인자의 모든 순열(permutation)의 리스트를 돌려줘요.
permutations "abc" == ["abc","bac","cba","bca","cab","acb"]
20.3 리스트 축소 (Reducing lists; folds)
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl은 이항 연산자, 시작 값(보통 연산자의 왼쪽 항등원), 그리고 리스트에 적용되어 이항 연산자로 리스트를 왼쪽에서 오른쪽으로 축소해요:
foldl f z [x1, x2, ..., xn] == (...((z `f` x1) `f` x2) `f`...) `f` xn
리스트는 유한해야 해요.
foldl' :: (a -> b -> a) -> a -> [b] -> a
foldl의 엄격(strict) 버전이에요.
foldl1 :: (a -> a -> a) -> [a] -> a
foldl1은 시작 값 인자가 없는 foldl의 변형이라 반드시 비어 있지 않은 리스트에 적용되어야 해요.
foldl1' :: (a -> a -> a) -> [a] -> a
foldl1의 엄격 버전이에요.
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr은 이항 연산자, 시작 값(보통 연산자의 오른쪽 항등원), 그리고 리스트에 적용되어 이항 연산자로 리스트를 오른쪽에서 왼쪽으로 축소해요:
foldr f z [x1, x2, ..., xn] == x1 `f` (x2 `f` ... (xn `f` z)...)
foldr1 :: (a -> a -> a) -> [a] -> a
foldr1은 시작 값 인자가 없는 foldr의 변형이라 반드시 비어 있지 않은 리스트에 적용되어야 해요.
특수 접기 (Special folds)
concat :: [[a]] -> [a]
리스트의 리스트를 이어붙여요.
concatMap :: (a -> [b]) -> [a] -> [b]
함수를 리스트에 매핑하고 결과를 이어붙여요.
and :: [Bool] -> Bool
and는 부울 리스트의 논리곱(conjunction)을 돌려줘요. 결과가 True가 되려면 리스트가 유한해야 하고, False는 유한하거나 무한한 리스트의 유한 인덱스에 있는 False 값에서 나와요.
or :: [Bool] -> Bool
or는 부울 리스트의 논리합(disjunction)을 돌려줘요. 결과가 False가 되려면 리스트가 유한해야 하고, True는 유한하거나 무한한 리스트의 유한 인덱스에 있는 True 값에서 나와요.
any :: (a -> Bool) -> [a] -> Bool
술어와 리스트에 적용되어, any는 리스트의 어떤 요소가 술어를 만족하는지 판정해요. 결과가 False가 되려면 리스트가 유한해야 하고, True는 유한하거나 무한한 리스트의 유한 인덱스에 있는 요소에 술어를 적용한 값이 True일 때 나와요.
all :: (a -> Bool) -> [a] -> Bool
술어와 리스트에 적용되어, all은 리스트의 모든 요소가 술어를 만족하는지 판정해요. 결과가 True가 되려면 리스트가 유한해야 하고, False는 유한하거나 무한한 리스트의 유한 인덱스에 있는 요소에 술어를 적용한 값이 False일 때 나와요.
sum :: Num a => [a] -> a
sum 함수는 유한한 숫자 리스트의 합을 계산해요.
product :: Num a => [a] -> a
product 함수는 유한한 숫자 리스트의 곱을 계산해요.
maximum :: Ord a => [a] -> a
maximum은 리스트에서 최댓값을 돌려줘요. 리스트는 비어 있지 않고 유한하며 정렬 가능한(ordered) 타입이어야 해요. 이것은 프로그래머가 자신의 비교 함수를 제공할 수 있는 maximumBy의 특수한 경우예요.
minimum :: Ord a => [a] -> a
minimum은 리스트에서 최솟값을 돌려줘요. 리스트는 비어 있지 않고 유한하며 정렬 가능한 타입이어야 해요. 이것은 프로그래머가 자신의 비교 함수를 제공할 수 있는 minimumBy의 특수한 경우예요.
20.4 리스트 만들기 (Building lists)
스캔 (Scans)
scanl :: (a -> b -> a) -> a -> [b] -> [a]
scanl은 foldl과 비슷하지만 왼쪽에서 연속적인 축소 값들의 리스트를 돌려줘요:
scanl f z [x1, x2, ...] == [z, z `f` x1, (z `f` x1) `f` x2, ...]
last (scanl f z xs) == foldl f z xs라는 점을 유의하세요.
scanl1 :: (a -> a -> a) -> [a] -> [a]
scanl1은 시작 값 인자가 없는 scanl의 변형이에요:
scanl1 f [x1, x2, ...] == [x1, x1 `f` x2, ...]
scanr :: (a -> b -> b) -> b -> [a] -> [b]
scanr은 scanl의 오른쪽에서 왼쪽으로 가는 쌍대(dual)예요. head (scanr f z xs) == foldr f z xs라는 점을 유의하세요.
scanr1 :: (a -> a -> a) -> [a] -> [a]
scanr1은 시작 값 인자가 없는 scanr의 변형이에요.
누적 맵 (Accumulating maps)
mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
mapAccumL 함수는 map과 foldl의 결합처럼 동작해요. 리스트의 각 요소에 함수를 적용하되 누적 매개변수(accumulating parameter)를 왼쪽에서 오른쪽으로 전달하고, 누적자의 최종 값과 함께 새 리스트를 돌려줘요.
mapAccumR :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
mapAccumR 함수는 map과 foldr의 결합처럼 동작해요. 리스트의 각 요소에 함수를 적용하되 누적 매개변수를 오른쪽에서 왼쪽으로 전달하고, 누적자의 최종 값과 함께 새 리스트를 돌려줘요.
무한 리스트 (Infinite lists)
iterate :: (a -> a) -> a -> [a]
iterate f x는 f를 x에 반복 적용한 무한 리스트를 돌려줘요:
iterate f x == [x, f x, f (f x), ...]
repeat :: a -> [a]
repeat x는 모든 요소의 값이 x인 무한 리스트예요.
replicate :: Int -> a -> [a]
replicate n x는 모든 요소의 값이 x인 길이 n의 리스트예요. 이것은 n이 어떤 정수 타입이든 될 수 있는 더 일반적인 Data.List.genericReplicate의 한 인스턴스예요.
cycle :: [a] -> [a]
cycle은 유한 리스트를 순환 리스트로 묶어요. 즉 원래 리스트의 무한 반복과 동등해요. 무한 리스트에 대해서는 항등 함수예요.
펼치기 (Unfolding)
unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
unfoldr 함수는 foldr의 '쌍대(dual)'예요. foldr이 리스트를 요약값으로 축소하는 반면, unfoldr은 시드(seed) 값에서 리스트를 만듭니다. 함수는 요소를 받아 리스트 생산을 끝냈다면 Nothing을 돌려주거나, Just (a,b)를 돌려주는데 이 경우 a가 리스트에 앞에 추가되고 b가 재귀 호출의 다음 요소로 사용돼요. 예를 들어,
iterate f == unfoldr (\x -> Just (x, f x))
어떤 경우에는 unfoldr이 foldr 연산을 되돌릴 수 있어요:
unfoldr f' (foldr f z xs) == xs
다음이 성립한다면요:
f' (f x y) = Just (x,y)
f' z = Nothing
unfoldr의 간단한 사용:
unfoldr (\b -> if b == 0 then Nothing else Just (b, b-1)) 10
[10,9,8,7,6,5,4,3,2,1]
20.5 부분 리스트 (Sublists)
부분 리스트 추출 (Extracting sublists)
take :: Int -> [a] -> [a]
take n을 리스트 xs에 적용하면 xs의 길이 n 접두부(prefix)를 돌려주거나, n > length xs이면 xs 자체를 돌려줘요:
take 5 "Hello World!" == "Hello"
take 3 [1,2,3,4,5] == [1,2,3]
take 3 [1,2] == [1,2]
take 3 [] == []
take (-1) [1,2] == []
take 0 [1,2] == []
이것은 n이 어떤 정수 타입이든 될 수 있는 더 일반적인 Data.List.genericTake의 한 인스턴스예요.
drop :: Int -> [a] -> [a]
drop n xs는 앞의 n개 요소 뒤의 xs 접미부(suffix)를 돌려주거나, n > length xs이면 []를 돌려줘요:
drop 6 "Hello World!" == "World!"
drop 3 [1,2,3,4,5] == [4,5]
drop 3 [1,2] == []
drop 3 [] == []
drop (-1) [1,2] == [1,2]
drop 0 [1,2] == [1,2]
이것은 n이 어떤 정수 타입이든 될 수 있는 더 일반적인 Data.List.genericDrop의 한 인스턴스예요.
splitAt :: Int -> [a] -> ([a], [a])
splitAt n xs는 첫 요소가 xs의 길이 n 접두부이고 두 번째 요소가 리스트의 나머지인 튜플을 돌려줘요:
splitAt 6 "Hello World!" == ("Hello ","World!")
splitAt 3 [1,2,3,4,5] == ([1,2,3],[4,5])
splitAt 1 [1,2,3] == ([1],[2,3])
splitAt 3 [1,2,3] == ([1,2,3],[])
splitAt 4 [1,2,3] == ([1,2,3],[])
splitAt 0 [1,2,3] == ([],[1,2,3])
splitAt (-1) [1,2,3] == ([],[1,2,3])
이것은 (take n xs, drop n xs)와 동등해요. splitAt은 n이 어떤 정수 타입이든 될 수 있는 더 일반적인 Data.List.genericSplitAt의 한 인스턴스예요.
takeWhile :: (a -> Bool) -> [a] -> [a]
takeWhile을 술어 p와 리스트 xs에 적용하면 p를 만족하는 요소들의 xs의 가장 긴 접두부(비어 있을 수도 있음)를 돌려줘요:
takeWhile (< 3) [1,2,3,4,1,2,3,4] == [1,2]
takeWhile (< 9) [1,2,3] == [1,2,3]
takeWhile (< 0) [1,2,3] == []
dropWhile :: (a -> Bool) -> [a] -> [a]
dropWhile p xs는 takeWhile p xs 뒤에 남는 접미부를 돌려줘요:
dropWhile (< 3) [1,2,3,4,5,1,2,3] == [3,4,5,1,2,3]
dropWhile (< 9) [1,2,3] == []
dropWhile (< 0) [1,2,3] == [1,2,3]
span :: (a -> Bool) -> [a] -> ([a], [a])
span을 술어 p와 리스트 xs에 적용하면 첫 요소가 p를 만족하는 요소들의 xs의 가장 긴 접두부(비어 있을 수도 있음)이고 두 번째 요소가 리스트의 나머지인 튜플을 돌려줘요:
span (< 3) [1,2,3,4,1,2,3,4] == ([1,2],[3,4,1,2,3,4])
span (< 9) [1,2,3] == ([1,2,3],[])
span (< 0) [1,2,3] == ([],[1,2,3])
span p xs는 (takeWhile p xs, dropWhile p xs)와 동등해요.
break :: (a -> Bool) -> [a] -> ([a], [a])
break를 술어 p와 리스트 xs에 적용하면 첫 요소가 p를 만족하지 않는 요소들의 xs의 가장 긴 접두부(비어 있을 수도 있음)이고 두 번째 요소가 리스트의 나머지인 튜플을 돌려줘요:
break (> 3) [1,2,3,4,1,2,3,4] == ([1,2,3],[4,1,2,3,4])
break (< 9) [1,2,3] == ([],[1,2,3])
break (> 9) [1,2,3] == ([1,2,3],[])
break p는 span (not . p)와 동등해요.
stripPrefix :: Eq a => [a] -> [a] -> Maybe [a]
stripPrefix 함수는 주어진 접두부를 리스트에서 제거해요. 리스트가 주어진 접두부로 시작하지 않으면 Nothing을 돌려주고, 시작하면 접두부 뒤의 리스트를 Just로 돌려줘요.
stripPrefix "foo" "foo" == Just ""
stripPrefix "foo" "foobar" == Just "bar"
stripPrefix "foo" "barfoo" == Nothing
stripPrefix "foo" "barfoobaz" == Nothing
group :: Eq a => [a] -> [[a]]
group 함수는 리스트를 받아 결과의 이어붙임(concatenation)이 인자와 같은 리스트의 리스트를 돌려줘요. 게다가 결과의 각 부분 리스트는 같은 요소만을 포함해요. 예를 들어,
group "Mississippi" = ["M","i","ss","i","ss","i","pp","i"]
이것은 프로그래머가 자신의 동등성 검사를 제공할 수 있는 groupBy의 특수한 경우예요.
inits :: [a] -> [[a]]
inits 함수는 인자의 모든 초기 구간(initial segment)을 가장 짧은 것부터 돌려줘요. 예를 들어,
inits "abc" == ["","a","ab","abc"]
tails :: [a] -> [[a]]
tails 함수는 인자의 모든 끝 구간(final segment)을 가장 긴 것부터 돌려줘요. 예를 들어,
tails "abc" == ["abc", "bc", "c",""]
20.6 술어 (Predicates)
isPrefixOf :: Eq a => [a] -> [a] -> Bool
isPrefixOf 함수는 두 리스트를 받아 첫 리스트가 두 번째 리스트의 접두부이면 True를 돌려줘요.
isSuffixOf :: Eq a => [a] -> [a] -> Bool
isSuffixOf 함수는 두 리스트를 받아 첫 리스트가 두 번째 리스트의 접미부이면 True를 돌려줘요. 두 리스트 모두 유한해야 해요.
isInfixOf :: Eq a => [a] -> [a] -> Bool
isInfixOf 함수는 두 리스트를 받아 첫 리스트가 완전히 온전한 상태로 두 번째 리스트 어딘가에 포함되어 있으면 True를 돌려줘요. 예:
isInfixOf "Haskell" "I really like Haskell." == True
isInfixOf "Ial" "I really like Haskell." == False
20.7 리스트 검색 (Searching lists)
동등성으로 검색 (Searching by equality)
elem :: Eq a => a -> [a] -> Bool
elem은 리스트 소속 술어로, 보통 중위(infix) 형태로 써요. 예를 들어 x elem xs. 결과가 False가 되려면 리스트가 유한해야 하고, True는 유한하거나 무한한 리스트의 유한 인덱스에서 x와 같은 요소를 찾을 때 나와요.
notElem :: Eq a => a -> [a] -> Bool
notElem은 elem의 부정이에요.
lookup :: Eq a => a -> [(a, b)] -> Maybe b
lookup key assocs는 연관 리스트에서 키를 찾아요.
술어로 검색 (Searching with a predicate)
find :: (a -> Bool) -> [a] -> Maybe a
find 함수는 술어와 리스트를 받아 리스트에서 술어와 맞는 첫 번째 요소를 돌려주거나, 그런 요소가 없으면 Nothing을 돌려줘요.
filter :: (a -> Bool) -> [a] -> [a]
filter를 술어와 리스트에 적용하면 술어를 만족하는 요소들의 리스트를 돌려줘요. 즉,
filter p xs = [ x | x <- xs, p x]
partition :: (a -> Bool) -> [a] -> ([a], [a])
partition 함수는 술어와 리스트를 받아 술어를 만족하는 요소들의 리스트와 만족하지 않는 요소들의 리스트의 쌍을 돌려줘요. 즉,
partition p xs == (filter p xs, filter (not . p) xs)
20.8 리스트 인덱싱 (Indexing lists)
(!!) :: [a] -> Int -> a
0부터 시작하는 리스트 인덱스(첨자) 연산자예요. 이것은 어떤 정수 타입의 인덱스를 받는 더 일반적인 Data.List.genericIndex의 한 인스턴스예요.
elemIndex :: Eq a => a -> [a] -> Maybe Int
elemIndex 함수는 주어진 리스트에서 ==로 쿼리 요소와 같은 첫 번째 요소의 인덱스를 돌려주거나, 그런 요소가 없으면 Nothing을 돌려줘요.
elemIndices :: Eq a => a -> [a] -> [Int]
elemIndices 함수는 쿼리 요소와 같은 모든 요소의 인덱스를 오름차순으로 돌려줌으로써 elemIndex를 확장해요.
findIndex :: (a -> Bool) -> [a] -> Maybe Int
findIndex 함수는 술어와 리스트를 받아 리스트에서 술어를 만족하는 첫 번째 요소의 인덱스를 돌려주거나, 그런 요소가 없으면 Nothing을 돌려줘요.
findIndices :: (a -> Bool) -> [a] -> [Int]
findIndices 함수는 술어를 만족하는 모든 요소의 인덱스를 오름차순으로 돌려줌으로써 findIndex를 확장해요.
20.9 리스트 지퍼·언지퍼 (Zipping and unzipping lists)
zip :: [a] -> [b] -> [(a, b)]
zip은 두 리스트를 받아 대응하는 쌍들의 리스트를 돌려줘요. 입력 리스트 중 하나가 짧으면 더 긴 리스트의 초과 요소는 버려져요.
zip3 :: [a] -> [b] -> [c] -> [(a, b, c)]
zip3은 세 리스트를 받아 zip과 유사한 삼중(triple) 리스트를 돌려줘요.
zip4 :: [a] -> [b] -> [c] -> [d] -> [(a, b, c, d)]
zip4 함수는 네 리스트를 받아 zip과 유사한 사중(quadruple) 리스트를 돌려줘요.
zip5 :: [a] -> [b] -> [c] -> [d] -> [e] -> [(a, b, c, d, e)]
zip5 함수는 다섯 리스트를 받아 zip과 유사한 오중(five-tuple) 리스트를 돌려줘요.
zip6 :: [a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [(a, b, c, d, e, f)]
zip6 함수는 여섯 리스트를 받아 zip과 유사한 육중(six-tuple) 리스트를 돌려줘요.
zip7 :: [a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [g] -> [(a, b, c, d, e, f, g)]
zip7 함수는 일곱 리스트를 받아 zip과 유사한 칠중(seven-tuple) 리스트를 돌려줘요.
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith는 튜플링 함수 대신 첫 번째 인자로 주어진 함수로 지퍼링함으로써 zip을 일반화해요. 예를 들어 zipWith (+)는 두 리스트에 적용해 대응하는 합들의 리스트를 만드는 데 사용돼요.
zipWith3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
zipWith3 함수는 세 요소를 결합하는 함수와 세 리스트를 받아 zipWith와 유사한 그들의 점별(point-wise) 결합 리스트를 돌려줘요.
zipWith4 :: (a -> b -> c -> d -> e) -> [a] -> [b] -> [c] -> [d] -> [e]
zipWith4 함수는 네 요소를 결합하는 함수와 네 리스트를 받아 zipWith와 유사한 그들의 점별 결합 리스트를 돌려줘요.
zipWith5 :: (a -> b -> c -> d -> e -> f) -> [a] -> [b] -> [c] -> [d] -> [e] -> [f]
zipWith5 함수는 다섯 요소를 결합하는 함수와 다섯 리스트를 받아 zipWith와 유사한 그들의 점별 결합 리스트를 돌려줘요.
zipWith6 :: (a -> b -> c -> d -> e -> f -> g) -> [a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [g]
zipWith6 함수는 여섯 요소를 결합하는 함수와 여섯 리스트를 받아 zipWith와 유사한 그들의 점별 결합 리스트를 돌려줘요.
zipWith7 :: (a -> b -> c -> d -> e -> f -> g -> h) -> [a] -> [b] -> [c] -> [d] -> [e] -> [f] -> [g] -> [h]
zipWith7 함수는 일곱 요소를 결합하는 함수와 일곱 리스트를 받아 zipWith와 유사한 그들의 점별 결합 리스트를 돌려줘요.
unzip :: [(a, b)] -> ([a], [b])
unzip은 쌍들의 리스트를 첫 번째 성분들의 리스트와 두 번째 성분들의 리스트로 변환해요.
unzip3 :: [(a, b, c)] -> ([a], [b], [c])
unzip3 함수는 삼중들의 리스트를 받아 unzip과 유사한 세 리스트를 돌려줘요.
unzip4 :: [(a, b, c, d)] -> ([a], [b], [c], [d])
unzip4 함수는 사중들의 리스트를 받아 unzip과 유사한 네 리스트를 돌려줘요.
unzip5 :: [(a, b, c, d, e)] -> ([a], [b], [c], [d], [e])
unzip5 함수는 오중들의 리스트를 받아 unzip과 유사한 다섯 리스트를 돌려줘요.
unzip6 :: [(a, b, c, d, e, f)] -> ([a], [b], [c], [d], [e], [f])
unzip6 함수는 육중들의 리스트를 받아 unzip과 유사한 여섯 리스트를 돌려줘요.
unzip7 :: [(a, b, c, d, e, f, g)] -> ([a], [b], [c], [d], [e], [f], [g])
unzip7 함수는 칠중들의 리스트를 받아 unzip과 유사한 일곱 리스트를 돌려줘요.
20.10 특수 리스트 (Special lists)
문자열 함수 (Functions on strings)
lines :: String -> [String]
lines은 문자열을 줄바꿈 문자에서 문자열들의 리스트로 나눠요. 결과 문자열들은 줄바꿈을 포함하지 않아요.
words :: String -> [String]
words는 문자열을 공백으로 구분된 단어들의 리스트로 나눠요.
unlines :: [String] -> String
unlines은 lines의 역연산이에요. 각 줄에 종결 줄바꿈을 추가한 뒤 줄들을 이어붙여요.
unwords :: [String] -> String
unwords는 words의 역연산이에요. 단어들을 분리 공백으로 이어붙여요.
“집합” 연산 (“Set” operations)
nub :: Eq a => [a] -> [a]
O(n^2). nub 함수는 리스트에서 중복 요소를 제거해요. 특히 각 요소의 첫 번째 발생만 유지해요. (이름 nub은 '본질(essence)'을 뜻해요.) 이것은 프로그래머가 자신의 동등성 검사를 제공할 수 있는 nubBy의 특수한 경우예요.
delete :: Eq a => a -> [a] -> [a]
delete x는 리스트 인자에서 x의 첫 번째 발생을 제거해요. 예를 들어,
delete 'a' "banana" == "bnana"
이것은 프로그래머가 자신의 동등성 검사를 제공할 수 있는 deleteBy의 특수한 경우예요.
(\\) :: Eq a => [a] -> [a] -> [a]
\\ 함수는 리스트 차(list difference)예요(결합 법칙이 아님). 결과 xs \\ ys에서는 ys의 각 요소의 첫 번째 발생(있을 경우)이 차례로 xs에서 제거돼요. 따라서 (xs ++ ys) \\ xs == ys예요. 이것은 프로그래머가 자신의 동등성 검사를 제공할 수 있는 deleteFirstsBy의 특수한 경우예요.
union :: Eq a => [a] -> [a] -> [a]
union 함수는 두 리스트의 리스트 합집합을 돌려줘요. 예를 들어,
"dog" `union` "cow" == "dogcw"
중복과 첫 리스트의 요소는 두 번째 리스트에서 제거되지만, 첫 리스트에 중복이 있으면 결과에도 중복이 생겨요. 이것은 프로그래머가 자신의 동등성 검사를 제공할 수 있는 unionBy의 특수한 경우예요.
intersect :: Eq a => [a] -> [a] -> [a]
intersect 함수는 두 리스트의 리스트 교집합을 취해요. 예를 들어,
[1,2,3,4] `intersect` [2,4,6,8] == [2,4]
첫 리스트에 중복이 있으면 결과에도 중복이 생겨요.
[1,2,2,3,4] `intersect` [6,4,4,2] == [2,2,4]
이것은 프로그래머가 자신의 동등성 검사를 제공할 수 있는 intersectBy의 특수한 경우예요.
정렬된 리스트 (Ordered lists)
sort :: Ord a => [a] -> [a]
sort 함수는 안정적(stable) 정렬 알고리즘을 구현해요. 이것은 프로그래머가 자신의 비교 함수를 제공할 수 있는 sortBy의 특수한 경우예요.
insert :: Ord a => a -> [a] -> [a]
insert 함수는 요소와 리스트를 받아, 다음 요소보다 여전히 작거나 같은 마지막 위치에 요소를 삽입해요. 특히 호출 전에 리스트가 정렬되어 있으면 결과도 정렬돼요. 이것은 프로그래머가 자신의 비교 함수를 제공할 수 있는 insertBy의 특수한 경우예요.
20.11 일반화된 함수 (Generalized functions)
” By ” 연산 (The "By" operations)
사용자 제공 동등성 (User-supplied equality; replacing an Eq context)
nubBy :: (a -> a -> Bool) -> [a] -> [a]
nubBy 함수는 오버로드된 == 함수 대신 사용자 제공 동등성 술어를 사용한다는 점 외에는 nub과 똑같이 동작해요.
deleteBy :: (a -> a -> Bool) -> a -> [a] -> [a]
deleteBy 함수는 사용자 제공 동등성 술어를 취한다는 점 외에는 delete처럼 동작해요.
deleteFirstsBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
deleteFirstsBy 함수는 술어와 두 리스트를 받아, 두 번째 리스트의 각 요소의 첫 번째 발생을 제거한 첫 리스트를 돌려줘요.
unionBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
unionBy 함수는 union의 비오버로드 버전이에요.
intersectBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]
intersectBy 함수는 intersect의 비오버로드 버전이에요.
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy 함수는 group의 비오버로드 버전이에요.
사용자 제공 비교 (User-supplied comparison; replacing an Ord context)
sortBy :: (a -> a -> Ordering) -> [a] -> [a]
sortBy 함수는 sort의 비오버로드 버전이에요.
insertBy :: (a -> a -> Ordering) -> a -> [a] -> [a]
insert의 비오버로드 버전이에요.
maximumBy :: (a -> a -> Ordering) -> [a] -> a
maximumBy 함수는 비교 함수와 리스트를 받아 비교 함수로 보아 리스트에서 가장 큰 요소를 돌려줘요. 리스트는 유한하고 비어 있지 않아야 해요.
minimumBy :: (a -> a -> Ordering) -> [a] -> a
minimumBy 함수는 비교 함수와 리스트를 받아 비교 함수로 보아 리스트에서 가장 작은 요소를 돌려줘요. 리스트는 유한하고 비어 있지 않아야 해요.
” generic ” 연산 (The "generic" operations)
genericLength :: Num i => [b] -> i
genericLength 함수는 length의 오버로드 버전이에요. 특히 Int를 돌려주는 대신 Num의 인스턴스인 어떤 타입이든 돌려줘요. 다만 length보다는 덜 효율적이에요.
genericTake :: Integral i => i -> [a] -> [a]
genericTake 함수는 취할 요소 수로 어떤 Integral 값이든 받는 take의 오버로드 버전이에요.
genericDrop :: Integral i => i -> [a] -> [a]
genericDrop 함수는 버릴 요소 수로 어떤 Integral 값이든 받는 drop의 오버로드 버전이에요.
genericSplitAt :: Integral i => i -> [b] -> ([b], [b])
genericSplitAt 함수는 분리 위치로 어떤 Integral 값이든 받는 splitAt의 오버로드 버전이에요.
genericIndex :: Integral a => [b] -> a -> b
genericIndex 함수는 인덱스로 어떤 Integral 값이든 받는 !!의 오버로드 버전이에요.
genericReplicate :: Integral i => i -> a -> [a]
genericReplicate 함수는 반복 횟수로 어떤 Integral 값이든 받는 replicate의 오버로드 버전이에요.
더 알아보기 (Learn more)
- 대부분의 함수는 술어나 비교 함수를 받는
By계열(nubBy,sortBy,groupBy등)과generic계열(genericLength,genericTake등)로 일반화돼요. - 문자열 조작(
lines,words등)과 세트 연산(nub,union,intersect등)은 실무에서 자주 쓰이는 함수들이에요.