Functional Programming - Functions

Conditional Expressions

다른 언어들과 마찬가지로 Haskell도 조건식이 존재합니다. 다만 statement가 아닌 expression을 이용합니다.

abs :: Int -> Int  
abs n = if n >= 0 then n else -n  

위의 예는 절대값을 만드는 간단한 function입니다. 아래와 같이 nested로 표현도 가능합니다.

signum :: Int -> Int  
signum n = if n < 0 then -1 else  
             if n == 0 then 0 else 1

(Haskell에서는 애매한(ambiguous) 경우를 제거하기 위해 else가 필수입니다.)

Guarded Equations

위와 같은 조건식을 사용하는 것과 달리 guarded equation이라는 것을 이용하여 표현이 가능합니다.

abs n | n >= 0    = n  
      | otherwise = -n

위의 예는 앞서 본 abs와 같은 결과를 가져옵니다. 바로 비교가 필요한 function의 경우 guarded equation을 이용하여 표현이 가능합니다. signum의 경우도 아래와 같이 더 이해하기 쉽게 표현할 수 있습니다.

signum n | n < 0     = -1  
         | n == 0    = 0
         | otherwise = 1

(otherwise의 경우 else와 마찬가지로 꼭 포함이 되어있어야합니다. otherwise = True라고 표현해야만 하죠.)

Pattern Matching

현재 대부분 언어들이 pattern matching을 지원합니다. (Scala의 경우 case, C#의 경우 switch가 한 예입니다.) Haskell에서는 다른 언어들과 달리 바로 적용이 가능합니다.

not       :: Bool -> Bool  
not False = True  
not True  = False  

위의 예는 not을 pattern matching 통해 구현한 것입니다. 아래도 같은 예입니다.

(&&)           :: Bool -> Bool -> Bool
True && True   = True  
True && False  = False  
False && True  = False  
False && False = False  

위와 같은 경우 아래와 같이 줄여서 표현도 가능합니다.

True && True = True  
_    && _    = False  

모두 True의 경우만 True이기 때문에 나머지 경우는 모두 False로 처리하면 됩니다. ( _(underscore)의 경우 어떠한 variable이라는 표현입니다.) 위의 경우도 좋지만 아래와 같이 표현하는 것이 더 좋습니다.

True && b = b  
False && _ = False  

True의 경우 b까지 체크하여 b를 리턴하지만, False의 경우 무엇을 넣어도 False 이므로 False를 만날 경우 뒤까지 연산하지 않고 False를 리턴합니다. 아래는 pattern matching의 경우 주의해야 하는 사항입니다.

  • 모든 pattern matching은 위에서 아래로 해석됩니다.
  • pattern matching에 이용된 variable은 모두 unique해야 합니다. (ex. b && b = b X)

List Patterns

Haskell의 모든 list는 내부적으로 (:) operator를 중복 이용하여 list를 만듭니다. [1,2,3,4]의 경우 1:(2:(3:4:[])))의 동작이 내부에서 발생하게 되는 것이죠. 따라서 내부에서 patten matching이 발생하는 것이죠. 여기서 말하는 pattern 은 (:) operator를 이용한 표현이라고 생각됩니다. (저는 솔직히 이게 이해가 잘 안갑니다. 하지만 다음 예를 보면 이해하기 쉬울 겁니다.)

(x:xs) pattern을 이용하여 List에 사용되는 function들을 보면서 추가적인 설명을 진행하겠습니다.

head       :: [a] -> a  
head (x:_) = x

tail        :: [a] -> [a]  
tail (_:xs) = xs  

head (x:_)의 경우 앞의 x만 신경쓰면 되고 tail (_:xs)의 경우는 뒤의 값만을 신경쓰게 됩니다. (여기서 '신경'이라는 부분이 Complier가 해석하는 부분이하고 생각하시면 될 듯 합니다.)
(x:xs) pattern에 대해 추가적인 주의 사항입니다.

  • (x:xs)는 non-empty list에 대해 적용이 가능합니다. 따라서 head []는 에러를 발생합니다.
  • 또한 (x:xs) pattern은 사용시 괄호(parenthesis)와 함께 사용되야 합니다. 이유는 앞에서 다뤘듯이 function의 우선순위가 높기 때문입니다. 따라서 head x:_ = x의 경우 head xx:_보다 먼저 적용되게 됩니다.

Lambda Expressions

Java를 포함한 최근 사용되는 많은 언어들이 lambda를 지원합니다. 간단히 lambda expression은 이름이 없이 구현된 function을 이야기합니다. 자세한 사항은 계속 보도록 하겠습니다. Haskell에서는 다음과 같이 표현합니다.

\x -> x + x

\는 lambda를 의미하며 위의 경우 x를 두번 더하는 결과를 돌려줍니다. 사실 다른 언어에서의 lambda는 다소 closure에 가깝습니다. 이유는 다른 scope에 있는 변수들에 가깝기 때문입니다. 따라서 side-effect를 고려해야 합니다. (물론 그 둘의 차이를 느끼기엔 제가 내공이 부족합니다.)
그럼 lambda가 왜 유용할까요? 다음 예를 보면서 설명하겠습니다.

add x y = x + y

add = \x -> (\y -> x + y)  

위는 전에 다뤘던 currying을 이용한 구현이고 아래는 lambda를 이용하였습니다. 아래의 경우가 다소 더 길지만 차근차근 읽기에는 더 좋습니다. currying과 같이 함수 명 뒤에 띄어쓰기(whitespace)로 매개변수를 표현하는 것보다는 훨씬 function의 형태를 보여줍니다.

const     :: a -> b ->c  
const x _ = x

const   :: a -> (b -> c)  
const x = \_ -> x  

위의 경우(currying) 다소 내부적으로 const x가 function을 돌려줄 것이라고 생각해야하지만, 아래 경우(lambda) const x_ -> x 형태의 function을 돌려주는 것을 직관적으로 이해할 수 있습니다.
또한, lambda는 이름이 없는 function으로 번거로운 naming을 제거합니다.

odd n = map f [0..n-1]  
        where
            f x = x*2 + 1

위와 같은 구현을 아래와 같이 작성할 수 있습니다.

odd n = map (\x -> x*2 + 1) [0..n-1]  

lambda를 이용하여 where로 표현하던 f를 제거할 수 있었습니다.

Sections

Sections은 두개의 argument 사이에 사용되는 operator를 curried function처럼 앞쪽(infix)에 작성하는 방식입니다. (괄호와 함께 말이죠.)

> 1+2
3

> (+) 1 2
3  

이러한 표현은 operator와 argument를 합쳐 표현하는 것도 가능합니다.

> (1+) 2
3

> (+2) 1
3  

따라서, 앞에 예와 같이 + operator에 대해 (+), (x+), (+y)와 같이 부분 적용(partially applied)하는 방법에 대해 section이라고 말합니다.
그럼 왜 section이 유용할까요?
다음과 같이 간단한 기능을 가진 function을 표현할 수 있기 때문입니다.

(1+) -- successor function
(1/) -- reciprocation function
(*2) -- doubling function
(/2) -- halving function

이 경우 lambda를 사용하지 않아도 되며, 불필요한 naming또한 피할 수 있습니다.

Reference

comments powered by Disqus