Haskell 함수by Pigbrain

패턴 매칭

  • 패턴 매칭은 어떤 데이터가 따라야 할 패턴을 지정한다
  • 패턴에 따라 데이터를 분해하기 위하여 사용한다
  1. # lucky.hs
  2. lucky :: Int -> String
  3. lucky 7 = "LUKCY NUMBER SEVEN!"
  4. lucky x = "Sorry"
  5.  
  6. ghci>lucky 3
  7. "Sorry"
  8. ghci>lucky 7
  9. "LUKCY NUMBER SEVEN!"
  • lucky를 호출하면 위부터 아래까지 패턴을 검사한다
  1. # factorial.hs
  2. factorial :: Int -> Int
  3. factorial 0 = 1
  4. factorial n = n * factorial(n - 1)
  5.  
  6. ghci>factorial(3)
  7. 6
  • 재귀를 이용하여 factorial함수를 정의할 수 있다
  1. # addVectors.hs
  2. addVectors :: (Double, Double) -> (Double, Double) -> (Double, Double)
  3. addVectors a b = (fst a + fst b, snd a + snd b)
  4.  
  5. ghci>addVectors (1, 3) (2, 4)
  6. (3.0,7.0)
  • 튜플을 이용하여 패턴 매칭이 가능하다
  1. # first.hs
  2. first :: (a, b, c) -> a
  3. first (x, _, _) = x
  4.  
  5. # second.hs
  6. second :: (a, b, c) -> b
  7. second (_, y, _) = y
  8.  
  9. ghci>first (1, 2, 3)
  10. 1
  11. ghci>second (1, 2, 3)
  12. 2
  • _ 는 리스트 Comprehension에서 작업을 수행하는 것과 같다
  • _ 부분은 어떠한 값이 들어와도 상관 없다는 것을 의미한다
  1. # head.hs
  2. head' :: [a] -> a
  3. head' [] = error "Can't call head on an empty list"
  4. head' (x:_) = x
  5.  
  6. ghci>head' [1, 2, 3]
  7. 1
  • 일반적인 리스트도 패턴 매칭에서 사용될 수 있다
  • [1, 2, 3]은 1:2:3:[]에 대한 syntactic sugar이다
  • (x:[])와 (x:y:[])는 [x], [x,y]로 쓸 수 있다
  • : 문자를 포함하는 패턴은 하나 이상의 길이인 리스트에 대해서만 매칭된다
  1. # firstLetter.h
  2. firstLetter :: String -> String
  3. firstLetter "" = "Empty string"
  4. firstLetter all@(x:xs) = "The first letther of " ++ all ++ " is " ++ [x]
  5.  
  6. ghci>firstLetter "Hello World"
  7. "The first letther of Hello World is H"
  • as-패턴은 전체 원본 항목에 대한 참조를 유지한다
  • as-패턴 xs@(x:y:ys)는 x:y:ys와 같은 리스트에 매칭되며 매번 x:y:ys라고 입력하는 대신에 xs를 이용하여 전체 데이터를 사용할 수 있다

가드(Guard)

  • 가드는 함수에 전달된 값들을 검사하기 위하여 사용한다
  1. # bmiTell.hs
  2. bmiTell :: Double -> String
  3. bmiTell bmi
  4. | bmi <= 18.5 = "you're underweight"
  5. | bmi <= 25.0 = "normal"
  6. | otherwise = "whale"
  7.  
  8. ghci>bmiTell 18.5
  9. "you're underweight"
  10. ghci>bmiTell 20
  11. "normal"
  • 가드는 파이프 라인() 다음에 Boolean 표현식을 쓰고 표현식이 True일 경우 사용될 함수의 내용을 작성한다
  • 가드는 적어도 한 칸 이상 들여쓰기가 되어야 한다

where

  • where 키워드를 사용하여 중간 계산 결과를 저장할 수 있다
    • 함수의 끝에서 변수와 바인딩 된다
  • where에서 정의한 변수는 가드를 포함한 함수 전체에서 사용 가능하다
  1. # bmiTell.hs
  2. bmiTell :: Double -> Double -> String
  3. bmiTell weight height
  4. | weight / height ^ 2 <= 18.5 = "you're underweight"
  5. | weight / height ^ 2 <= 25.0 = "normal"
  6. | otherwise = "whale"
  7.  
  8.  
  9. # bmiTell.hs (using where keyword)
  10. bmiTell :: Double -> Double ->String
  11. bmiTell weight height
  12. | bmi <= 18.5 = "you're underweight"
  13. | bmi <= 25.0 = "normal"
  14. | otherwise = "whale"
  15. where bmi = weight / height ^ 2
  • 첫번째 코드는 bmi계산을 반복하고 있지만 두번째 코드는 where 키워드를 이용하여 bmi 변수에 계산 결과를 바인딩한다
  1. calcBmis :: [(Double, Double)] -> [Double]
  2. calcBmis xs = [bmi w h | (w, h) <- xs]
  3. where bmi weight height = weight / height ^ 2
  • where 블럭 속에 함수를 정의할 수 있다
  1. # calcBmis.hs
  2. calcBmis :: [(Double, Double)] -> [Double]
  3. calcBmis xs = [bmi w h | (w, h) <- xs]
  4. where bmi weight height = weight / height ^ 2
  5. ghci> calcBmis [(4, 2)]
  6. [1.0]

let

  • let 표현식은 where와 매우 비슷하다
  • let 표현식은 적용 범위가 매우 작아서 가드에서는 보이지 않는다
  • let으로 정의한 변수는 let 표현식 내에서만 보이게 된다
  1. # cylinder.hs
  2. cylinder :: Double -> Double -> Double
  3. cylinder r h =
  4. let sideArea = 2 * pi * r * h
  5. topArea = pi * r ^ 2
  6. in sideArea + 2 * topArea
  7. ghci>cylinder 4 5
  8. 226.1946710584651
  • let 표현식은 로컬 영역의 함수를 실행하기 위하여 사용될 수 있다
  1. ghci> [let square x = x * x in (square 5, square 3, square 2)]
  2. [(25,9,4)]
  • let 표현식은 세미콜론으로 구분될 수 있다.
    • 여러 가지 변수들을 바인딩하고자 할 때 유용하다
    • 줄을 바꿔서 열에 맞춰 정렬하면 안 된다
  1. ghci> (let a = 100; b = 200; c = 300 in a * b * c, let foo = "Hey "; bar = "there!" in foo ++ bar)
  2. (6000000,"Hey there!")
  • let 표현식으로 다음과 같이 튜플의 요소들을 해체하고 바인딩하기 위한 패턴 매칭을 할 수 있다
  1. ghci> (let (a, b, c) = (1, 2, 3) in a + b + c) * 100
  2. 600
  • let 표현식은 리스트 Comprehension에서도 사용할 수 있다
  1. # calcBmis.hs
  2. calcBmis :: [(Double, Double)] -> [Double]
  3. calcBmis xs = [bmi | (w, h) <- xs, let bmi = w / h ^2]

case

  • case 표현식은 특정 변수의 특정 값에 대한 코드 블록을 실행할 수 있게 해준다
  1. # head.hs
  2. head' :: [a] -> a
  3. head' xs = case xs of [] -> error "No head for empty list!"
  4. (x:_) -> x
  5. ghci> head' "Hello World"
  6. 'H'
  • 함수 정의부에서의 패턴 매칭은 case 표현식을 사용하는 것과 동일하다
  1. describeList :: [a] -> String
  2. describeList ls = "The list is " ++ what ls
  3. where what [] = "empty."
  4. what [x] = "a singleton list."
  5. what xs = "a longer list."

참고

  • http://www.yes24.com/24/Goods/12155304?Acode=101
Published 15 January 2017