93
하하하 하하하하하 하하 하하하 하하 2016 하 5 하 14 하 하하하

하스켈 프로그래밍 입문

Embed Size (px)

Citation preview

Page 1: 하스켈 프로그래밍 입문

하스켈 프로그래밍 입문하스켈 학교 2016 년 5 월 14 일서광열

Page 2: 하스켈 프로그래밍 입문

출처 : https://wiki.haskell.org/Haskell_logos/New_logo_ideas

Page 3: 하스켈 프로그래밍 입문

출처 : http://i.imgur.com/GHxmrsR.jpg

Page 4: 하스켈 프로그래밍 입문

교재

Page 5: 하스켈 프로그래밍 입문

역사1930s

Alonzo Church

람다 대수1950s

John McCarthy 의 Lisp

1960s

Peter Landin’s ISWIM

첫 번째 순수 함수 언어

1970s

John Backus’s FP

고차함수와 프로그램의 이해를 강조한 함수 언어

1970s

Robin Milner 의 ML

첫 번째 현대적인 함수 언어타입 추론과 polymorphic 타입 제공

1970s - 1980s

David Turner 의 Miranda

lazy 함수 언어

Page 6: 하스켈 프로그래밍 입문

역사1987 년하스켈PL 연구자들이 모여서 만든 표준 함수 언어

1990s:

Phil Wadler

타입 클래스와 모나드

2003

하스켈 언어 명세 Haskell Report 출간2010

개정 버전 출간

Page 7: 하스켈 프로그래밍 입문

중요 특징• Purely functional

• Statically typed

• Lazy

출처 : https://xkcd.com/1312/

Page 8: 하스켈 프로그래밍 입문

프로그래밍 스타일

int total = 0;

for (int i = 1; i <= 10; i++)

total = total + i;

sum [1..10]

Page 9: 하스켈 프로그래밍 입문

Glasgow Haskell Compiler

• 하스켈 컴파일러 (ghc) 와 인터프리터 (ghci) 제공• www.haskell.org/platform

Page 10: 하스켈 프로그래밍 입문

GHCi• REPL (Read-Eval-Print Loop)• 터미널에서 ghci 를 실행$ ghci

GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help

Prelude>

Page 11: 하스켈 프로그래밍 입문

Expression> 2+3*4

14

> (2+3)*4

20

> negate 3

-3

Page 12: 하스켈 프로그래밍 입문

Standard Prelude

> head [1,2,3,4]

1

> tail [1,2,3,4]

[2,3,4]

> [1,2,3,4] !! 2

3

• 하스켈의 표준 라이브러리• GHCi 를 실행하면 자동으로 로딩

Page 13: 하스켈 프로그래밍 입문

> take 3 [1,2,3,4,5]

[1,2,3]

> drop 3 [1,2,3,4,5]

[4,5]

> length [1,2,3,4,5]

5

Page 14: 하스켈 프로그래밍 입문

> sum [1,2,3,4,5]

15

> product [1,2,3,4,5]

120

> [1,2,3] ++ [4,5]

[1,2,3,4,5]

> reverse [1,2,3,4,5]

[5,4,3,2,1]

Page 15: 하스켈 프로그래밍 입문

유용한 GHCi 명령명령 의미

:load name 스크립트를 로드:reload 현재 스크립트를 다시 로드

:type expr expr 의 타입을 보여줌:? 모든 명령을 보여줌

:quit GHCi 를 종료

Page 16: 하스켈 프로그래밍 입문

Function Application• 함수 f 와 인자 a

• f a

• 함수 f 와 인자 a, b

• f a b• 함수 f 와 인자 a, b, c

• f a b c• 함수 f, g 와 인자 a, b

• f a + g b

Page 17: 하스켈 프로그래밍 입문

하스켈 스크립트• 함수를 정의할 수 있음• .hs 확장자를 가짐-- test.hs

main = putStrLn "Hello World!"

$ ghc test.hs -o test

$./test

"Hello World"

Page 18: 하스켈 프로그래밍 입문

하스켈 함수 정의-- test.hs

double x = x + x

square x = x * x

$ ghci test.hs

Prelude> double 10

20

Prelude> square 10

100

Page 19: 하스켈 프로그래밍 입문

하스켈 함수 정의fac n = product [1..n]

average ns = sum ns `div` length ns

Page 20: 하스켈 프로그래밍 입문

Naming• 요구사항

• 함수와 인자 이름은 소문자• myFun, fun1, arg_2, x'

• 타입은 대문자• Bool, Char, String

• 컨벤션• 리스트 인자는 보통 s 를 끝에 붙임

• xs, ns, nss

Page 21: 하스켈 프로그래밍 입문

레이아웃 규칙좋은 예

a = 10

b = 20

c = 30

나쁜 예

a = 10

b = 20

c = 30

나쁜 예

a = 10

b = 20

c = 30

Page 22: 하스켈 프로그래밍 입문

레이아웃 규칙a = b + c

where

b = 1

c = 2

d = a * 2

Page 23: 하스켈 프로그래밍 입문

타입• 연관된 값들의 집합• 예 ) Bool 타입은 True 값과 False 값을 가짐

Page 24: 하스켈 프로그래밍 입문

타입 오류> 1 + False

<interactive>:16:3:

No instance for (Num Bool) arising from a use of '+'

In the expression: 1 + False

In an equation for 'it': it = 1 + False

Page 25: 하스켈 프로그래밍 입문

하스켈의 타입• expression e 를 계산했을 때 타입 t 가 나오면 e :: t라고 표기함• 모든 well formed expression 을 타입을 가지며 , 컴파일 타임에 type inference 를 통해 자동으로 계산됨

Page 26: 하스켈 프로그래밍 입문

타입 확인하기> not False

True

> :type not False

not False :: Bool

Page 27: 하스켈 프로그래밍 입문

하스켈의 기본 타입타입 값Bool 논리값Char 문자

String 문자열Int [-2^29 .. 2^29-1] 사이의 정수

Integer 임의의 정수Float 부동소수점

Page 28: 하스켈 프로그래밍 입문

리스트 타입• 리스트 : 같은 타입을 가지는 값의 나열

• [False, True, False] :: [Bool]

• ['a', 'b', 'c', 'd'] :: [Char]

• [['a'], ['b', 'c']] :: [[Char]]

• [t] 는 t 를 원소로 가지는 리스트 타입

Page 29: 하스켈 프로그래밍 입문

리스트 타입

출처 : http://learnyouahaskell.com/starting-out

Page 30: 하스켈 프로그래밍 입문

튜플 타입• 튜플 : 서로 다른 타입을 가지는 값의 나열

• (False, True) :: (Bool, Bool)

• (False, 'a', True) :: (Bool, Char, Bool)

• ('a', (False, ('b')) :: (Char, (Bool, Char))

• (True, ['a', 'b']) :: (Bool, [Char])

• (t1, t2, …, tn) 은 j 번째 원소가 tj 타입인 n 튜플 타입

Page 31: 하스켈 프로그래밍 입문

함수 타입• 함수 : 한 타입의 값을 다른 타입의 값으로 매핑

• not :: Bool -> Bool

• even :: Int -> Bool

• t1 -> t2 는 타입 t1 의 값을 타입 t2 의 값으로 매핑하는 함수의 타입

Page 32: 하스켈 프로그래밍 입문

함수 타입add :: (Int, Int) -> Int

add (x,y) = x + y

zeroto :: Int -> [Int]

zeroto n = [0..n]

Page 33: 하스켈 프로그래밍 입문

Curried 함수• curried 함수 : 함수를 결과로 리턴하여 인자 하나짜리 함수로 인자 여러 개의 함수를 표현add :: (Int, Int) -> Int

add (x, y) = x + y

add' :: Int -> (Int -> Int)

add' x y = x + y

Page 34: 하스켈 프로그래밍 입문

Partial Application• Curried function 에 인자를 일부만 적용해서 새로운 함수를 만들 수 있음

• add' 1 :: Int -> Int

• take 5 :: [Int] -> [Int]

• drop 5 :: [Int] -> [Int]

Page 35: 하스켈 프로그래밍 입문

Currying 의 컨벤션• 괄호가 너무 많아지는 것을 방지하기 위해 -> 는 right-

associative 함• Int -> Int -> Int -> Int

• Int -> (Int -> (Int -> Int))

Page 36: 하스켈 프로그래밍 입문

Curried 함수의 application

• 반대로 function application 은 left associative함• 따라서 mult x y z 는 (((mult x) y) z) 를 의미함• 튜플을 명시적으로 사용하지 않으면 모든 하스켈 함수는 기본적으로 curried 함수가 됨

Page 37: 하스켈 프로그래밍 입문

타입 변수

> :t head

head :: [a] -> a

> :t fst

fst :: (a, b) -> a

• 타입 변수는 소문자 a, b, c, … 로 표시• 어떤 타입이든 될 수 있음

Page 38: 하스켈 프로그래밍 입문

Polymorphic 함수• polymorphic 함수 : 타입이 하나 이상의 타입 변수를 포함

• length :: [a] -> Int

• 모든 타입 a 에 대해서 [a] 를 주면 Int 를 리턴함

Page 39: 하스켈 프로그래밍 입문

Polymorphic 함수> length [False,True]

2

> length [1,2,3,4]

4

> length ['a','b','c']

3

> length ["hello","world"]

2

Page 40: 하스켈 프로그래밍 입문

Prelude 에 정의된 Polymorphic 함수

• fst :: (a, b) -> a

• head :: [a] -> a

• take :: Int -> [a] -> [a]

• zip :: [a] -> [b] -> [(a, b)]

• id :: a -> a

Page 41: 하스켈 프로그래밍 입문

Overloaded 함수• overloaded 함수 : 타입이 하나 이상의 class

constraint 를 가지고 있는 polymorphic 한 함수• (+) :: Num a => a -> a -> a

Page 42: 하스켈 프로그래밍 입문

Overloaded 함수 (+)> 1 + 2

3

> 1.0 + 2.0

3.0

> 'a' + 'b'

ERROR

Page 43: 하스켈 프로그래밍 입문

Overloaded 함수 (==)

> 5 == 5

True

> 5 /= 5

False

> 'a' == 'a'

True

> "Ho Ho" == "Ho Ho"

True

> 3.432 == 3.432

True

Page 44: 하스켈 프로그래밍 입문

Overloaded 함수 read

> read "5" :: Int

5

> read "5" :: Float

5.0

> (read "[1,2,3,4]" :: [Int])

[1,2,3,4]

> (read "(3, 'a')" :: (Int, Char))

(3, 'a')

Page 45: 하스켈 프로그래밍 입문

하스켈의 타입클래스들• Num: 숫자 타입

• (+) :: Num a -> a -> a -> a

• Eq: 같은지 비교가 가능한 타입• (==) :: Eq a => a -> a -> Bool

• Ord: 순서가 비교 가능한 타입• (<) :: Ord a => a -> a -> Bool

Page 46: 하스켈 프로그래밍 입문

하스켈의 타입클래스들• Show: 해당 타입을 문자열로 변환

• show :: Show a => a -> String

• Read: 문자열을 해당 타입으로 변환• read :: Read a => String -> a

Page 47: 하스켈 프로그래밍 입문

Overloaded number literal

• 하스켈의 상수는 여러 타입을 가짐> : t 123

123 :: Num a => a

> :t (123 :: Int)

(123 :: Int) :: Int

> :t (123 :: Double)

(123 :: Double) :: Double

> :t (123 :: Int)

(123 :: Int) :: Int

Page 48: 하스켈 프로그래밍 입문

조건 expression

abs :: Int -> Int

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

signum :: Int -> Int

signum n = if n <0 then -1 else

if n == 0 then 0 else 1

• 하스켈에서 항상 값을 리턴해야 하기 때문에 else 구문을 생략할 수 없음

Page 49: 하스켈 프로그래밍 입문

let 과 where

let y = a*b

f x = (x+y)/y

in f c + f d

f x y | y>z = ...

| y==z = ...

| y<z = ...

where z = x*x

Page 50: 하스켈 프로그래밍 입문

Guardabs n | n >= 0 = n

| otherwise = -n

signum n | n < 0 = -1

| n == 0 = 0

| otherwise = 1

Page 51: 하스켈 프로그래밍 입문

패턴 매칭not :: Bool -> Bool

not False = True

not True = False

(&&) :: Bool -> Bool -> Bool

True && True = True

_ && _ = False

Page 52: 하스켈 프로그래밍 입문

잘못된 패턴 매칭(&&) :: Bool -> Bool -> Bool

_ && _ = False

True && True = True

Page 53: 하스켈 프로그래밍 입문

리스트 패턴• 리스트 [1,2,3,4] 는 1:(2:(3:(4:[])))

• (:) 는 cons 연산자라고 불림head :: [a] -> a

head (x:_) = x

tail :: [a] -> [a]

tail (_:xs) = xs

Page 54: 하스켈 프로그래밍 입문

Lambda expression

• lambda expression 을 이용하면 이름 없는 함수를 생성할 수 있음• \x -> x + x

• 인자 x 를 받아서 x + x 를 리턴하는 함수

Page 55: 하스켈 프로그래밍 입문

Lambda expressionadd x y = x + y

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

const :: a -> b -> a

const x _ = x

const :: a -> (b -> a)

const x = \_ -> x

Page 56: 하스켈 프로그래밍 입문

Lambda expressionodds n = map f [0..n-1]

where

f x = x * 2 + 1

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

Page 57: 하스켈 프로그래밍 입문

Section• 1 + 2 는 (+) 1 2 로 쓸 수 있음• 인자 중에 하나만 제공해서 새로운 함수를 만들 수도 있음> (1+) 2

3

> (+2) 1

Page 58: 하스켈 프로그래밍 입문

Section 예제• (1+)

• (1/)

• (*2)

• (/2)

Page 59: 하스켈 프로그래밍 입문

재귀 함수• 하스켈에서 함수는 자기 자신으로 정의 가능한데 이런 함수를 재귀 함수라고 부름

fac 0 = 1

fac n = n * fac (n-1)

Page 60: 하스켈 프로그래밍 입문

fac 함수의 실행fac 3

= 3 * fac 2

= 3 * (2 * fac 1)

= 3 * (2 * (1 * fac 0))

= 3 * (2 * (1 * 1))

= 3* (2 * 1)

= 3 * 2

= 6

Page 61: 하스켈 프로그래밍 입문

재귀 함수의 장점• 일부 함수는 재귀로 정의하는 것이 더 쉬움• 재귀로 정의한 함수는 mathematical induction 을 이용해 property 를 증명할 수 있음

Page 62: 하스켈 프로그래밍 입문

리스트에 대한 재귀product :: Num a => [a] -> a

product [] = 1

product (n:ns) = n * product ns

length :: [a] -> int

length [] = 0

length (_:xs) = 1 + length xs

Page 63: 하스켈 프로그래밍 입문

product 함수의 실행product [2,3,4]

= 2 * product [3,4]

= 2 * (3 * product [4])

= 2 * (3 * (4 * product []))

= 2 * (3 * (4 * 1))

= 24

Page 64: 하스켈 프로그래밍 입문

length 함수의 실행length [1,2,3]

= 1 + length [2,3]

= 1 + (1 + length [3])

= 1 + (1 + (1 + length []))

= 1 + (1 + (1 + 0))

= 3

Page 65: 하스켈 프로그래밍 입문

리스트에 대한 재귀reverse :: [a] -> [a]

reverse [] = []

reverse (x:xs) = reverse xs ++ [x]

Page 66: 하스켈 프로그래밍 입문

reverse 함수의 실행reverse [1,2,3]

= reverse [2,3] ++ [1]

= (reverse [3] ++ [2]) ++ [1]

= ((reverse [] ++ [3]) ++ [2]) ++ [1]

= (([] ++ [3]) ++ [2]) ++ [1]

= [3,2,1]

Page 67: 하스켈 프로그래밍 입문

인자가 여러 개인 재귀 함수zip :: [a] -> [b] -> [(a,b)]

zip [] _ = []

zip _ [] = []

zip (x:xs) (y:ys) = (x, y) : zip xs ys

Page 68: 하스켈 프로그래밍 입문

리스트에 대한 재귀drop :: Int -> [a] -> [a]

drop 0 xs = xs

drop _ [] = []

drop n (_:xs) = drop (n-1) xs

(++) :: [a] -> [a] -> [a]

[] ++ ys = ys

(x:xs) ++ ys = x:(xs ++ ys)

Page 69: 하스켈 프로그래밍 입문

append 시각화xs = [0, 1, 2]

ys = [3,4,5]

zs = xs ++ ys

Page 70: 하스켈 프로그래밍 입문

퀵소트qsort :: Ord a => [a] -> [a]

qsort (x:xs) =

qsort smaller ++ [x] ++ qsort larger

where

smaller = [a | a <- xs, a <= x]

larger = [b | b <- xs, b > x]

Page 71: 하스켈 프로그래밍 입문

퀵소트q [3,2,4,1,5]

++ [3] ++q [2,1] q [4,5]

++ [2] ++q [1] q [] q [] ++ [4] ++ q [5]

[1] [5]

Page 72: 하스켈 프로그래밍 입문

고차함수• 고차함수 : 함수를 인자로 받거나 함수를 결과로 리턴하는 함수

twice :: (a -> a) -> a -> a

twice f x = f (f x)

Page 73: 하스켈 프로그래밍 입문

map 함수• map :: (a -> b) -> [a] -> [b]

> map (+1) [1,3,5,7]

[2,4,6,8]

Page 74: 하스켈 프로그래밍 입문

map 함수의 정의-- list comprehension

map f xs = [f x | x <- xs]

-- recursive definition

map f [] = []

map f (x:xs) = f x : map f xs

Page 75: 하스켈 프로그래밍 입문

filter 함수• filter :: (a -> Bool) -> [a] -> [b]

> filter even [1..10]

[2,4,6,8,10]

Page 76: 하스켈 프로그래밍 입문

filter 함수의 정의-- list comprehension

filter p xs = [x | x <- xs, p x]

-- recursive definition

filter p [] = []

filter p (x:xs)

| p x = x : filter p xs

| otherwise = filter p xs

Page 77: 하스켈 프로그래밍 입문

foldr 함수• foldr 는 리스트에 대한 재귀의 패턴을 표현함

• f [] = v

• f (x:xs) = x ⊕ f xs

Page 78: 하스켈 프로그래밍 입문

직접 재귀 예제-- v = 0, ⊕ = +

sum [] = 0

sum (x:xs) = x + sum xs

-- v = 0, ⊕ = *

product [] = 1

product (x:xs) = x * product xs

-- v = 0, ⊕ = &&

and [] = True

and (x:xs) = x && and xs

Page 79: 하스켈 프로그래밍 입문

foldr 을 이용한 재귀 예제sum = foldr (+) 0

product = foldr (*) 1

or = foldr (||) false

and = foldr (&&) True

Page 80: 하스켈 프로그래밍 입문

foldr 의 정의foldr :: (a -> b -> b) -> b -> [a] ->b

foldr f v [] = v

foldr f v (x:xs) = f x (foldr f v xs)

Page 81: 하스켈 프로그래밍 입문

foldr 시각화

Page 82: 하스켈 프로그래밍 입문

sum 의 계산sum [1,2,3]

= foldr (+) 0 [1,2,3]

= foldr (+) 0 (1:(2:(3:[])))

= 1+(2+(3+0))

= 6

Page 83: 하스켈 프로그래밍 입문

product 의 계산product [1,2,3]

= foldr (*) 1 [1,2,3]

= foldr (*) 1 (1:(2:(3:[])))

= 1*(2*(3*1))

= 6

Page 84: 하스켈 프로그래밍 입문

foldr 를 이용한 length 함수

length = foldr (\_ n -> 1 + n) 0

length [1,2,3]

= length (1:(2:(3:[])))

= foldr (\_ n -> 1 + n) 0

Page 85: 하스켈 프로그래밍 입문

foldr 를 이용한 재귀 함수reverse = foldr (\x xs -> xs ++ [x]) []

(++ ys) = foldr (:) ys

Page 86: 하스켈 프로그래밍 입문

foldr 이 유용한 이유• 직접적인 재귀보다 정의하기 쉬움• 함수의 property 를 foldr 의 algebraic property를 이용하여 증명할 수 있음• 프로그램 최적화가 쉬워짐

Page 87: 하스켈 프로그래밍 입문

고차 함수의 예 takeWhile

takeWhile :: (a -> Bool) -> [a] -> [a]

takeWhile p [] = []

takeWhile p (x:xs)

| p x = x : takeWhile p xs

| otherwise = []

> takeWhile (/= ' ') "abc def"

"abc"

Page 88: 하스켈 프로그래밍 입문

참고 자료1. Slides of Programming in Haskell

• http://www.cs.nott.ac.uk/~pszgmh/book.html

2. Learn You a Haskell for Great Good!• http://learnyouahaskell.com/chapters

3. A Gentle Introduction to Haskell 98• https://www.haskell.org/tutorial/haskell-98-tut

orial.pdf

Page 89: 하스켈 프로그래밍 입문

연습 문제1. 리스트의 마지막 원소를 리턴하는 함수 myLast 를 작성하시오

• myLast :: [a] -> a

2. 리스트의 k 번째 원소를 리턴하는 함수 elementAt를 작성하시오• elementAt :: [a] -> Int -> a

Page 90: 하스켈 프로그래밍 입문

연습 문제• 리스트가 palindrome 인지 아닌지를 리턴하는 함수를 작성하시오

• isPalindrome :: (Eq a) => [a] -> Bool> isPalindrome [1,2,3]

False

> isPalindrome "madamimadam"

True

> isPalindrome [1,2,4,8,16,8,4,2,1]

True

Page 91: 하스켈 프로그래밍 입문

연습 문제• 연속된 리스트 원소를 제거하는 compress 함수를 작성하시오

• compress :: Eq a => [a] -> [a]

> compress "aaaabccaadeeee"

"abcade"

Page 92: 하스켈 프로그래밍 입문

연습 문제• 리스트를 run-length 인코딩으로 리턴하는 함수

encode 를 작성하시오• encode :: Eq a => [a] -> [(Int, a)]

> encode "aaaabccaadeeee"

[(4,'a'),(1,'b'),(2,'c'),(2,'a'),(1,'d'),(4,'e')]

Page 93: 하스켈 프로그래밍 입문

숙제• CIS 194 Homework 1,2,3,4,5 번을 풉니다

• http://www.seas.upenn.edu/~cis194/lectures.html