Skip to content

Haskell:HaskellBasics

하스켈 기초 (Haskell Basics)

환경 갖추기 (Getting set up)

변수와 함수 (Variables and functions)

heron a b c = sqrt (s*(s-a)*(s-b)*(s-c))
    where
    s = (a+b+c) / 2

진위값 (Truth values)

Prelude> 4 + 9 == 13
True
Prelude> (==) (4 + 9) 13
True
  • 가드 (Guard)
    • 절대값 함수는 다음과 같다:
      \(|x|=\begin{cases}x,&x\ge0\\-x,&x\lt0\end{cases}\)
    • 하스켈 코드는 다음과 같다:

abs x

| x < 0     = 0 - x
| otherwise = x

</syntaxhighlight>

    • 위의 코드가 대응하는 수학적 정의만큼이나 가독성 있다는 것에 주목하자. 정의를 하나씩 해부해보겠다.
      • 일반적인 함수 정의로 시작해, 함수 이름 abs를 쓰고, 이 함수가 단일 매개변수 x를 취한다고 알린다.
      • =와 정의의 우변을 적는 대신 줄 바꿈을 하고, 두 대체문을 별개의 줄에 놓았다. 이 대체문들은 가드 (Guard)라고 하는 것들이다. 공백은 단순히 미(美) 적인 이유로 넣은 것이 아니다. 공백은 코드가 올바르게 파싱되게 하는 필수 요소다.
      • 각각의 가드는 파이프 문자 |로 시작한다. 파이프 뒤에는 불리언 값으로 평가되는 표현식(불리언 조건식 또는 술어식(Predicate) 이라고도 부름)을 놓고, 그 다음 정의의 나머지 부분이 온다. 바로 술어식이 True로 평가될 경우 사용될, 등호와 우변이다.
      • otherwise 분기는 선행하는 술어식들 중 True로 평가되는 게 없을 때 쓰인다. 위의 경우, x가 0보다 작지 않으면 0과 같거나 0보다 커야만 한다. 따라서 마지막 술어식을 x >= 0으로 쓸 수도 있었지만, otherwise도 잘 작동한다.
  • where와 가드
    • where절은 특히 가드와 함께 쓸 때 편리하다. 2차 방정식 \(ax2+bx+c=0\) 의 (실수인) 해의 개수를 구하는 함수는 다음과 같다:

numOfSolutions a b c

| disc > 0  = 2
| disc == 0 = 1
| otherwise = 0
where
disc = b^2 - 4*a*c

</syntaxhighlight>

타입의 기초 (Type basics)

Prelude> :type True True :: Bool

</syntaxhighlight>

  • 문자는 Char이고, 문자열은 [Char]이다. 참고로 String[Char]은 동일하다.

Prelude> :t 'H' 'H' :: Char Prelude> :t "Hello World" "Hello World" :: [Char]

  • 함수 시그니처:
    • 예제로, xor의 타입을 확인하면 xor :: Bool -> Bool -> Bool로 출력된다.
    • ::의 좌측은 함수 이름.
    • ::의 우측은 함수의 인자 목록으로, -> 기호로 구분한다.
    • 단, -> 기호의 마지막에 있는 타입은 반환값 이다.
  • let .. in의 예제:

capitalize :: String -> String capitalize x =

let capWord []     = []
capWord (x:xs) = toUpper x : xs
in unwords (map capWord (words x))

</syntaxhighlight>

리스트와 튜플 (Lists and tuples)

Prelude> let numbers = [1,2,3,4] Prelude> let truths = [True, False, False] Prelude> let strings = ["here", "are", "some", "strings"]

</syntaxhighlight>

  • 각괄호는 리스트의 범위를 제한하고, 개개의 원소들은 쉼표로 구분된다.
  • 딱 하나 중요한 제한사항은 리스트 내의 모든 원소가 타입이 같아야 한다는 것이다.
  • 타입이 혼재된 원소들의 리스트를 정의하려고 하면 전형적인 타입 오류가 발생한다.
  • 리스트 컨싱 (Consing) [^0]
    • [1,2,3,4,5]1:2:3:4:5:[]는 완전히 동치다.
      • 하지만 리스트 생성에는 잠재적인 함정이 있다. True:False:[]와 같은 것은 더할 나위 없이 훌륭한 하스켈 코드지만 True:False는 아니다.
    • 문자열도 리스트일 뿐이다:

Prelude>"hey" == ['h','e','y'] True Prelude>"hey" == 'h':'e':'y':[] True

</syntaxhighlight>

  • 튜플:

(True, 1) ("Hello world", False) (4, 5, "Six", True, 'b')

</syntaxhighlight>

    • 튜플 변경불가능 (Immutable) 이다. 즉, 튜플에 컨싱(cons)할 수 없다. 따라서 몇 개의 값이 저장될지 미리 아는 경우에 튜플을 사용하는 것이 타당하다.
    • 튜플의 원소들은 같은 타입일 필요가 없다.

References

타입의 기초2 (Type basics II)

If/패턴/let

mySignum x =

if x < 0
then -1
else if x > 0
then 1
else 0

</syntaxhighlight>

    • 가드 문법으로 쉽게 재작성할 수 있다:

mySignum x

| x < 0     = -1
| x > 0     = 1
| otherwise = 0

</syntaxhighlight>

  • 패턴 매칭의 도입:

pts 1 = 10 pts 2 = 6 pts x

| x <= 6    = 7 - x
| otherwise = 0

</syntaxhighlight>

    • 또 다른 예시:

(||) :: Bool -> Bool -> Bool False || False = False _ || _ = True

</syntaxhighlight>

  • 튜플 패턴과 리스트 패턴:

-- 예: head, tail, 그리고 패턴 head :: [a] -> a head (x:_) = x head [] = error "Prelude.head: empty list"

tail :: [a] -> [a] tail (_:xs) = xs tail [] = error "Prelude.tail: empty list"

</syntaxhighlight>

  • let 바인딩
    • let 키워드를 선언 앞에 놓고, in을 이용해 함수의 "주" 몸체로 돌아온다는 신호를 보낸다. 하나의 let...in 블록 내에 여러 선언을 놓을 수 있다. 이것들이 같은 양만큼 들여쓰기 되었는지만 확실히 할 것. 그렇지 않으면 문법 오류가 발생한다:

roots a b c =

let sdisc = sqrt (b*b - 4*a*c)
twice_a = 2*a
in  ((-b + sdisc) / twice_a,
(-b - sdisc) / twice_a)

</syntaxhighlight>

어휘 쌓기 (Building vocabulary)

Prelude> f a = a + a
Prelude> k x = x * x
Prelude> f (k 10)
200
Prelude> k (f 10)
400

함수 합성 연산자인 (.)를 사용하면 된다.

Prelude> (f . k) 10
200
Prelude> (k . f) 10
400

간단한 입출력 (Simple input and output)

See also