하스켈 프로그래밍 입
문
하스켈 학교 2016년 5월 14일
서광열
출처: https://wiki.haskell.org/Haskell_logos/New_logo_ideas
출처: http://i.imgur.com/GHxmrsR.jpg
교재
역사
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 함수 언어
역사
1987년
하스켈
PL 연구자들이 모여서 만든 표준 함수 언어
1990s:
Phil Wadler
타입 클래스와 모나드
2003
하스켈 언어 명세 Haskell Report 출
간
2010
개정 버전 출간
중요 특징
• Purely functional
• Statically typed
• Lazy
출처: https://xkcd.com/1312/
프로그래밍 스타일
int total = 0;
for (int i = 1; i <= 10; i++)
total = total + i;
sum [1..10]
Glasgow Haskell Compiler
• 하스켈 컴파일러(ghc)와 인터프리터(ghci) 제공
• www.haskell.org/platform
GHCi
• REPL (Read-Eval-Print Loop)
• 터미널에서 ghci를 실행
$ ghci
GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help
Prelude>
Expression
> 2+3*4
14
> (2+3)*4
20
> negate 3
-3
Standard Prelude
> head [1,2,3,4]
1
> tail [1,2,3,4]
[2,3,4]
> [1,2,3,4] !! 2
3
• 하스켈의 표준 라이브러리
• GHCi를 실행하면 자동으로 로딩
> 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
> 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]
유용한 GHCi 명령
명령 의미
:load name 스크립트를 로드
:reload 현재 스크립트를 다시 로드
:type expr expr의 타입을 보여줌
:? 모든 명령을 보여줌
:quit GHCi를 종료
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
하스켈 스크립트
• 함수를 정의할 수 있음
• .hs 확장자를 가짐
-- test.hs
main = putStrLn "Hello World!"
$ ghc test.hs -o test
$./test
"Hello World"
하스켈 함수 정의
-- test.hs
double x = x + x
square x = x * x
$ ghci test.hs
Prelude> double 10
20
Prelude> square 10
100
하스켈 함수 정의
fac n = product [1..n]
average ns = sum ns `div` length ns
Naming
• 요구사항
• 함수와 인자 이름은 소문자
• myFun, fun1, arg_2, x'
• 타입은 대문자
• Bool, Char, String
• 컨벤션
• 리스트 인자는 보통 s를 끝에 붙임
• xs, ns, nss
레이아웃 규칙
좋은 예
a = 10
b = 20
c = 30
나쁜 예
a = 10
b = 20
c = 30
나쁜 예
a = 10
b = 20
c = 30
레이아웃 규칙
a = b + c
where
b = 1
c = 2
d = a * 2
타입
• 연관된 값들의 집합
• 예) Bool 타입은 True 값과 False 값을 가짐
타입 오류
> 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
하스켈의 타입
• expression e를 계산했을 때 타입 t가 나오면 e :: t라
고 표기함
• 모든 well formed expression을 타입을 가지며, 컴파
일 타임에 type inference를 통해 자동으로 계산됨
타입 확인하기
> not False
True
> :type not False
not False :: Bool
하스켈의 기본 타입
타입 값
Bool 논리값
Char 문자
String 문자열
Int [-2^29 .. 2^29-1] 사이의 정수
Integer 임의의 정수
Float 부동소수점
리스트 타입
• 리스트: 같은 타입을 가지는 값의 나열
• [False, True, False] :: [Bool]
• ['a', 'b', 'c', 'd'] :: [Char]
• [['a'], ['b', 'c']] :: [[Char]]
• [t]는 t를 원소로 가지는 리스트 타입
리스트 타입
출처: http://learnyouahaskell.com/starting-out
튜플 타입
• 튜플: 서로 다른 타입을 가지는 값의 나열
• (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튜플 타입
함수 타입
• 함수: 한 타입의 값을 다른 타입의 값으로 매핑
• not :: Bool -> Bool
• even :: Int -> Bool
• t1 -> t2는 타입 t1의 값을 타입 t2의 값으로 매핑하는
함수의 타입
함수 타입
add :: (Int, Int) -> Int
add (x,y) = x + y
zeroto :: Int -> [Int]
zeroto n = [0..n]
Curried 함수
• curried 함수: 함수를 결과로 리턴하여 인자 하나짜
리 함수로 인자 여러 개의 함수를 표현
add :: (Int, Int) -> Int
add (x, y) = x + y
add' :: Int -> (Int -> Int)
add' x y = x + y
Partial Application
• Curried function에 인자를 일부만 적용해서 새로운
함수를 만들 수 있음
• add' 1 :: Int -> Int
• take 5 :: [Int] -> [Int]
• drop 5 :: [Int] -> [Int]
Currying의 컨벤션
• 괄호가 너무 많아지는 것을 방지하기 위해 ->는
right-associative함
• Int -> Int -> Int -> Int
• Int -> (Int -> (Int -> Int))
Curried 함수의 application
• 반대로 function application은 left associative함
• 따라서 mult x y z는 (((mult x) y) z)를 의미함
• 튜플을 명시적으로 사용하지 않으면 모든 하스켈 함
수는 기본적으로 curried 함수가 됨
타입 변수
> :t head
head :: [a] -> a
> :t fst
fst :: (a, b) -> a
• 타입 변수는 소문자 a, b, c, …로 표시
• 어떤 타입이든 될 수 있음
Polymorphic 함수
• polymorphic 함수: 타입이 하나 이상의 타입 변수를
포함
• length :: [a] -> Int
• 모든 타입 a에 대해서 [a]를 주면 Int를 리턴함
Polymorphic 함수
> length [False,True]
2
> length [1,2,3,4]
4
> length ['a','b','c']
3
> length ["hello","world"]
2
Prelude에 정의된
Polymorphic 함수
• fst :: (a, b) -> a
• head :: [a] -> a
• take :: Int -> [a] -> [a]
• zip :: [a] -> [b] -> [(a, b)]
• id :: a -> a
Overloaded 함수
• overloaded 함수: 타입이 하나 이상의 class
constraint를 가지고 있는 polymorphic한 함수
• (+) :: Num a => a -> a -> a
Overloaded 함수 (+)
> 1 + 2
3
> 1.0 + 2.0
3.0
> 'a' + 'b'
ERROR
Overloaded 함수 (==)
> 5 == 5
True
> 5 /= 5
False
> 'a' == 'a'
True
> "Ho Ho" == "Ho Ho"
True
> 3.432 == 3.432
True
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')
하스켈의 타입클래스들
• Num: 숫자 타입
• (+) :: Num a -> a -> a -> a
• Eq: 같은지 비교가 가능한 타입
• (==) :: Eq a => a -> a -> Bool
• Ord: 순서가 비교 가능한 타입
• (<) :: Ord a => a -> a -> Bool
하스켈의 타입클래스들
• Show: 해당 타입을 문자열로 변환
• show :: Show a => a -> String
• Read: 문자열을 해당 타입으로 변환
• read :: Read a => String -> a
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
조건 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 구
문을 생략할 수 없음
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
Guard
abs n | n >= 0 = n
| otherwise = -n
signum n | n < 0 = -1
| n == 0 = 0
| otherwise = 1
패턴 매칭
not :: Bool -> Bool
not False = True
not True = False
(&&) :: Bool -> Bool -> Bool
True && True = True
_ && _ = False
잘못된 패턴 매칭
(&&) :: Bool -> Bool -> Bool
_ && _ = False
True && True = True
리스트 패턴
• 리스트 [1,2,3,4]는 1:(2:(3:(4:[])))
• (:)는 cons 연산자라고 불림
head :: [a] -> a
head (x:_) = x
tail :: [a] -> [a]
tail (_:xs) = xs
Lambda expression
• lambda expression을 이용하면 이름 없는 함수를 생
성할 수 있음
• x -> x + x
• 인자 x를 받아서 x + x를 리턴하는 함수
Lambda expression
add x y = x + y
add = x -> (y -> x + y)
const :: a -> b -> a
const x _ = x
const :: a -> (b -> a)
const x = _ -> x
Lambda expression
odds n = map f [0..n-1]
where
f x = x * 2 + 1
odd n = map (x -> x * 2 + 1) [0..n-1]
Section
• 1 + 2는 (+) 1 2로 쓸 수 있음
• 인자 중에 하나만 제공해서 새로운 함수를 만들 수도
있음
> (1+) 2
3
> (+2) 1
Section 예제
• (1+)
• (1/)
• (*2)
• (/2)
재귀 함수
• 하스켈에서 함수는 자기 자신으로 정의 가능한데 이
런 함수를 재귀 함수라고 부름
fac 0 = 1
fac n = n * fac (n-1)
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
재귀 함수의 장점
• 일부 함수는 재귀로 정의하는 것이 더 쉬움
• 재귀로 정의한 함수는 mathematical induction을 이
용해 property를 증명할 수 있음
리스트에 대한 재귀
product :: Num a => [a] -> a
product [] = 1
product (n:ns) = n * product ns
length :: [a] -> int
length [] = 0
length (_:xs) = 1 + length xs
product 함수의 실행
product [2,3,4]
= 2 * product [3,4]
= 2 * (3 * product [4])
= 2 * (3 * (4 * product []))
= 2 * (3 * (4 * 1))
= 24
length 함수의 실행
length [1,2,3]
= 1 + length [2,3]
= 1 + (1 + length [3])
= 1 + (1 + (1 + length []))
= 1 + (1 + (1 + 0))
= 3
리스트에 대한 재귀
reverse :: [a] -> [a]
reverse [] = []
reverse (x:xs) = reverse xs ++ [x]
reverse 함수의 실행
reverse [1,2,3]
= reverse [2,3] ++ [1]
= (reverse [3] ++ [2]) ++ [1]
= ((reverse [] ++ [3]) ++ [2]) ++ [1]
= (([] ++ [3]) ++ [2]) ++ [1]
= [3,2,1]
인자가 여러 개인 재귀 함
수
zip :: [a] -> [b] -> [(a,b)]
zip [] _ = []
zip _ [] = []
zip (x:xs) (y:ys) = (x, y) : zip xs ys
리스트에 대한 재귀
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)
append 시각화
xs = [0, 1, 2]
ys = [3,4,5]
zs = xs ++ ys
퀵소트
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]
퀵소트
q [3,2,4,1,5]
++ [3] ++q [2,1] q [4,5]
++ [2] ++q [1] q [] q [] ++ [4] ++ q [5]
[1] [5]
고차함수
• 고차함수: 함수를 인자로 받거나 함수를 결과로 리턴
하는 함수
twice :: (a -> a) -> a -> a
twice f x = f (f x)
map 함수
• map :: (a -> b) -> [a] -> [b]
> map (+1) [1,3,5,7]
[2,4,6,8]
map 함수의 정의
-- list comprehension
map f xs = [f x | x <- xs]
-- recursive definition
map f [] = []
map f (x:xs) = f x : map f xs
filter 함수
• filter :: (a -> Bool) -> [a] -> [b]
> filter even [1..10]
[2,4,6,8,10]
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
foldr 함수
• foldr는 리스트에 대한 재귀의 패턴을 표현함
• f [] = v
• f (x:xs) = x ⊕ f xs
직접 재귀 예제
-- 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
foldr을 이용한 재귀 예제
sum = foldr (+) 0
product = foldr (*) 1
or = foldr (||) false
and = foldr (&&) True
foldr의 정의
foldr :: (a -> b -> b) -> b -> [a] ->b
foldr f v [] = v
foldr f v (x:xs) = f x (foldr f v xs)
foldr 시각화
sum의 계산
sum [1,2,3]
= foldr (+) 0 [1,2,3]
= foldr (+) 0 (1:(2:(3:[])))
= 1+(2+(3+0))
= 6
product의 계산
product [1,2,3]
= foldr (*) 1 [1,2,3]
= foldr (*) 1 (1:(2:(3:[])))
= 1*(2*(3*1))
= 6
foldr를 이용한 length 함수
length = foldr (_ n -> 1 + n) 0
length [1,2,3]
= length (1:(2:(3:[])))
= foldr (_ n -> 1 + n) 0
foldr를 이용한 재귀 함수
reverse = foldr (x xs -> xs ++ [x]) []
(++ ys) = foldr (:) ys
foldr이 유용한 이유
• 직접적인 재귀보다 정의하기 쉬움
• 함수의 property를 foldr의 algebraic property를 이용
하여 증명할 수 있음
• 프로그램 최적화가 쉬워짐
고차 함수의 예
takeWhile
takeWhile :: (a -> Bool) -> [a] -> [a]
takeWhile p [] = []
takeWhile p (x:xs)
| p x = x : takeWhile p xs
| otherwise = []
> takeWhile (/= ' ') "abc def"
"abc"
참고 자료
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-
tutorial.pdf
연습 문제
1. 리스트의 마지막 원소를 리턴하는 함수 myLast를
작성하시오
• myLast :: [a] -> a
2. 리스트의 k번째 원소를 리턴하는 함수 elementAt
를 작성하시오
• elementAt :: [a] -> Int -> a
연습 문제
• 리스트가 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
연습 문제
• 연속된 리스트 원소를 제거하는 compress 함수를
작성하시오
• compress :: Eq a => [a] -> [a]
> compress "aaaabccaadeeee"
"abcade"
연습 문제
• 리스트를 run-length 인코딩으로 리턴하는 함수
encode를 작성하시오
• encode :: Eq a => [a] -> [(Int, a)]
> encode "aaaabccaadeeee"
[(4,'a'),(1,'b'),(2,'c'),(2,'a'),(1,'d'),(4,'e')]
숙제
• CIS 194 Homework 1,2,3,4,5번을 풉니다
• http://www.seas.upenn.edu/~cis194/lectures.html

하스켈 프로그래밍 입문

  • 1.
    하스켈 프로그래밍 입 문 하스켈학교 2016년 5월 14일 서광열
  • 2.
  • 3.
  • 4.
  • 5.
    역사 1930s Alonzo Church 람다 대수 1950s JohnMcCarthy의 Lisp 1960s Peter Landin’s ISWIM 첫 번째 순수 함수 언어 1970s John Backus’s FP 고차함수와 프로그램의 이해를 강 조한 함수 언어 1970s Robin Milner의 ML 첫 번째 현대적인 함수 언어 타입 추론과 polymorphic 타입 제공 1970s - 1980s David Turner의 Miranda lazy 함수 언어
  • 6.
    역사 1987년 하스켈 PL 연구자들이 모여서만든 표준 함수 언어 1990s: Phil Wadler 타입 클래스와 모나드 2003 하스켈 언어 명세 Haskell Report 출 간 2010 개정 버전 출간
  • 7.
    중요 특징 • Purelyfunctional • Statically typed • Lazy 출처: https://xkcd.com/1312/
  • 8.
    프로그래밍 스타일 int total= 0; for (int i = 1; i <= 10; i++) total = total + i; sum [1..10]
  • 9.
    Glasgow Haskell Compiler •하스켈 컴파일러(ghc)와 인터프리터(ghci) 제공 • www.haskell.org/platform
  • 10.
    GHCi • REPL (Read-Eval-PrintLoop) • 터미널에서 ghci를 실행 $ ghci GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help Prelude>
  • 11.
  • 12.
    Standard Prelude > head[1,2,3,4] 1 > tail [1,2,3,4] [2,3,4] > [1,2,3,4] !! 2 3 • 하스켈의 표준 라이브러리 • GHCi를 실행하면 자동으로 로딩
  • 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
  • 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]
  • 15.
    유용한 GHCi 명령 명령의미 :load name 스크립트를 로드 :reload 현재 스크립트를 다시 로드 :type expr expr의 타입을 보여줌 :? 모든 명령을 보여줌 :quit GHCi를 종료
  • 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
  • 17.
    하스켈 스크립트 • 함수를정의할 수 있음 • .hs 확장자를 가짐 -- test.hs main = putStrLn "Hello World!" $ ghc test.hs -o test $./test "Hello World"
  • 18.
    하스켈 함수 정의 --test.hs double x = x + x square x = x * x $ ghci test.hs Prelude> double 10 20 Prelude> square 10 100
  • 19.
    하스켈 함수 정의 facn = product [1..n] average ns = sum ns `div` length ns
  • 20.
    Naming • 요구사항 • 함수와인자 이름은 소문자 • myFun, fun1, arg_2, x' • 타입은 대문자 • Bool, Char, String • 컨벤션 • 리스트 인자는 보통 s를 끝에 붙임 • xs, ns, nss
  • 21.
    레이아웃 규칙 좋은 예 a= 10 b = 20 c = 30 나쁜 예 a = 10 b = 20 c = 30 나쁜 예 a = 10 b = 20 c = 30
  • 22.
    레이아웃 규칙 a =b + c where b = 1 c = 2 d = a * 2
  • 23.
    타입 • 연관된 값들의집합 • 예) Bool 타입은 True 값과 False 값을 가짐
  • 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
  • 25.
    하스켈의 타입 • expressione를 계산했을 때 타입 t가 나오면 e :: t라 고 표기함 • 모든 well formed expression을 타입을 가지며, 컴파 일 타임에 type inference를 통해 자동으로 계산됨
  • 26.
    타입 확인하기 > notFalse True > :type not False not False :: Bool
  • 27.
    하스켈의 기본 타입 타입값 Bool 논리값 Char 문자 String 문자열 Int [-2^29 .. 2^29-1] 사이의 정수 Integer 임의의 정수 Float 부동소수점
  • 28.
    리스트 타입 • 리스트:같은 타입을 가지는 값의 나열 • [False, True, False] :: [Bool] • ['a', 'b', 'c', 'd'] :: [Char] • [['a'], ['b', 'c']] :: [[Char]] • [t]는 t를 원소로 가지는 리스트 타입
  • 29.
  • 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튜플 타입
  • 31.
    함수 타입 • 함수:한 타입의 값을 다른 타입의 값으로 매핑 • not :: Bool -> Bool • even :: Int -> Bool • t1 -> t2는 타입 t1의 값을 타입 t2의 값으로 매핑하는 함수의 타입
  • 32.
    함수 타입 add ::(Int, Int) -> Int add (x,y) = x + y zeroto :: Int -> [Int] zeroto n = [0..n]
  • 33.
    Curried 함수 • curried함수: 함수를 결과로 리턴하여 인자 하나짜 리 함수로 인자 여러 개의 함수를 표현 add :: (Int, Int) -> Int add (x, y) = x + y add' :: Int -> (Int -> Int) add' x y = x + y
  • 34.
    Partial Application • Curriedfunction에 인자를 일부만 적용해서 새로운 함수를 만들 수 있음 • add' 1 :: Int -> Int • take 5 :: [Int] -> [Int] • drop 5 :: [Int] -> [Int]
  • 35.
    Currying의 컨벤션 • 괄호가너무 많아지는 것을 방지하기 위해 ->는 right-associative함 • Int -> Int -> Int -> Int • Int -> (Int -> (Int -> Int))
  • 36.
    Curried 함수의 application •반대로 function application은 left associative함 • 따라서 mult x y z는 (((mult x) y) z)를 의미함 • 튜플을 명시적으로 사용하지 않으면 모든 하스켈 함 수는 기본적으로 curried 함수가 됨
  • 37.
    타입 변수 > :thead head :: [a] -> a > :t fst fst :: (a, b) -> a • 타입 변수는 소문자 a, b, c, …로 표시 • 어떤 타입이든 될 수 있음
  • 38.
    Polymorphic 함수 • polymorphic함수: 타입이 하나 이상의 타입 변수를 포함 • length :: [a] -> Int • 모든 타입 a에 대해서 [a]를 주면 Int를 리턴함
  • 39.
    Polymorphic 함수 > length[False,True] 2 > length [1,2,3,4] 4 > length ['a','b','c'] 3 > length ["hello","world"] 2
  • 40.
    Prelude에 정의된 Polymorphic 함수 •fst :: (a, b) -> a • head :: [a] -> a • take :: Int -> [a] -> [a] • zip :: [a] -> [b] -> [(a, b)] • id :: a -> a
  • 41.
    Overloaded 함수 • overloaded함수: 타입이 하나 이상의 class constraint를 가지고 있는 polymorphic한 함수 • (+) :: Num a => a -> a -> a
  • 42.
    Overloaded 함수 (+) >1 + 2 3 > 1.0 + 2.0 3.0 > 'a' + 'b' ERROR
  • 43.
    Overloaded 함수 (==) >5 == 5 True > 5 /= 5 False > 'a' == 'a' True > "Ho Ho" == "Ho Ho" True > 3.432 == 3.432 True
  • 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')
  • 45.
    하스켈의 타입클래스들 • Num:숫자 타입 • (+) :: Num a -> a -> a -> a • Eq: 같은지 비교가 가능한 타입 • (==) :: Eq a => a -> a -> Bool • Ord: 순서가 비교 가능한 타입 • (<) :: Ord a => a -> a -> Bool
  • 46.
    하스켈의 타입클래스들 • Show:해당 타입을 문자열로 변환 • show :: Show a => a -> String • Read: 문자열을 해당 타입으로 변환 • read :: Read a => String -> a
  • 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
  • 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 구 문을 생략할 수 없음
  • 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
  • 50.
    Guard abs n |n >= 0 = n | otherwise = -n signum n | n < 0 = -1 | n == 0 = 0 | otherwise = 1
  • 51.
    패턴 매칭 not ::Bool -> Bool not False = True not True = False (&&) :: Bool -> Bool -> Bool True && True = True _ && _ = False
  • 52.
    잘못된 패턴 매칭 (&&):: Bool -> Bool -> Bool _ && _ = False True && True = True
  • 53.
    리스트 패턴 • 리스트[1,2,3,4]는 1:(2:(3:(4:[]))) • (:)는 cons 연산자라고 불림 head :: [a] -> a head (x:_) = x tail :: [a] -> [a] tail (_:xs) = xs
  • 54.
    Lambda expression • lambdaexpression을 이용하면 이름 없는 함수를 생 성할 수 있음 • x -> x + x • 인자 x를 받아서 x + x를 리턴하는 함수
  • 55.
    Lambda expression add xy = x + y add = x -> (y -> x + y) const :: a -> b -> a const x _ = x const :: a -> (b -> a) const x = _ -> x
  • 56.
    Lambda expression odds n= map f [0..n-1] where f x = x * 2 + 1 odd n = map (x -> x * 2 + 1) [0..n-1]
  • 57.
    Section • 1 +2는 (+) 1 2로 쓸 수 있음 • 인자 중에 하나만 제공해서 새로운 함수를 만들 수도 있음 > (1+) 2 3 > (+2) 1
  • 58.
    Section 예제 • (1+) •(1/) • (*2) • (/2)
  • 59.
    재귀 함수 • 하스켈에서함수는 자기 자신으로 정의 가능한데 이 런 함수를 재귀 함수라고 부름 fac 0 = 1 fac n = n * fac (n-1)
  • 60.
    fac 함수의 실행 fac3 = 3 * fac 2 = 3 * (2 * fac 1) = 3 * (2 * (1 * fac 0)) = 3 * (2 * (1 * 1)) = 3* (2 * 1) = 3 * 2 = 6
  • 61.
    재귀 함수의 장점 •일부 함수는 재귀로 정의하는 것이 더 쉬움 • 재귀로 정의한 함수는 mathematical induction을 이 용해 property를 증명할 수 있음
  • 62.
    리스트에 대한 재귀 product:: Num a => [a] -> a product [] = 1 product (n:ns) = n * product ns length :: [a] -> int length [] = 0 length (_:xs) = 1 + length xs
  • 63.
    product 함수의 실행 product[2,3,4] = 2 * product [3,4] = 2 * (3 * product [4]) = 2 * (3 * (4 * product [])) = 2 * (3 * (4 * 1)) = 24
  • 64.
    length 함수의 실행 length[1,2,3] = 1 + length [2,3] = 1 + (1 + length [3]) = 1 + (1 + (1 + length [])) = 1 + (1 + (1 + 0)) = 3
  • 65.
    리스트에 대한 재귀 reverse:: [a] -> [a] reverse [] = [] reverse (x:xs) = reverse xs ++ [x]
  • 66.
    reverse 함수의 실행 reverse[1,2,3] = reverse [2,3] ++ [1] = (reverse [3] ++ [2]) ++ [1] = ((reverse [] ++ [3]) ++ [2]) ++ [1] = (([] ++ [3]) ++ [2]) ++ [1] = [3,2,1]
  • 67.
    인자가 여러 개인재귀 함 수 zip :: [a] -> [b] -> [(a,b)] zip [] _ = [] zip _ [] = [] zip (x:xs) (y:ys) = (x, y) : zip xs ys
  • 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)
  • 69.
    append 시각화 xs =[0, 1, 2] ys = [3,4,5] zs = xs ++ ys
  • 70.
    퀵소트 qsort :: Orda => [a] -> [a] qsort (x:xs) = qsort smaller ++ [x] ++ qsort larger where smaller = [a | a <- xs, a <= x] larger = [b | b <- xs, b > x]
  • 71.
    퀵소트 q [3,2,4,1,5] ++ [3]++q [2,1] q [4,5] ++ [2] ++q [1] q [] q [] ++ [4] ++ q [5] [1] [5]
  • 72.
    고차함수 • 고차함수: 함수를인자로 받거나 함수를 결과로 리턴 하는 함수 twice :: (a -> a) -> a -> a twice f x = f (f x)
  • 73.
    map 함수 • map:: (a -> b) -> [a] -> [b] > map (+1) [1,3,5,7] [2,4,6,8]
  • 74.
    map 함수의 정의 --list comprehension map f xs = [f x | x <- xs] -- recursive definition map f [] = [] map f (x:xs) = f x : map f xs
  • 75.
    filter 함수 • filter:: (a -> Bool) -> [a] -> [b] > filter even [1..10] [2,4,6,8,10]
  • 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
  • 77.
    foldr 함수 • foldr는리스트에 대한 재귀의 패턴을 표현함 • f [] = v • f (x:xs) = x ⊕ f xs
  • 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
  • 79.
    foldr을 이용한 재귀예제 sum = foldr (+) 0 product = foldr (*) 1 or = foldr (||) false and = foldr (&&) True
  • 80.
    foldr의 정의 foldr ::(a -> b -> b) -> b -> [a] ->b foldr f v [] = v foldr f v (x:xs) = f x (foldr f v xs)
  • 81.
  • 82.
    sum의 계산 sum [1,2,3] =foldr (+) 0 [1,2,3] = foldr (+) 0 (1:(2:(3:[]))) = 1+(2+(3+0)) = 6
  • 83.
    product의 계산 product [1,2,3] =foldr (*) 1 [1,2,3] = foldr (*) 1 (1:(2:(3:[]))) = 1*(2*(3*1)) = 6
  • 84.
    foldr를 이용한 length함수 length = foldr (_ n -> 1 + n) 0 length [1,2,3] = length (1:(2:(3:[]))) = foldr (_ n -> 1 + n) 0
  • 85.
    foldr를 이용한 재귀함수 reverse = foldr (x xs -> xs ++ [x]) [] (++ ys) = foldr (:) ys
  • 86.
    foldr이 유용한 이유 •직접적인 재귀보다 정의하기 쉬움 • 함수의 property를 foldr의 algebraic property를 이용 하여 증명할 수 있음 • 프로그램 최적화가 쉬워짐
  • 87.
    고차 함수의 예 takeWhile takeWhile:: (a -> Bool) -> [a] -> [a] takeWhile p [] = [] takeWhile p (x:xs) | p x = x : takeWhile p xs | otherwise = [] > takeWhile (/= ' ') "abc def" "abc"
  • 88.
    참고 자료 1. Slidesof 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- tutorial.pdf
  • 89.
    연습 문제 1. 리스트의마지막 원소를 리턴하는 함수 myLast를 작성하시오 • myLast :: [a] -> a 2. 리스트의 k번째 원소를 리턴하는 함수 elementAt 를 작성하시오 • elementAt :: [a] -> Int -> a
  • 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
  • 91.
    연습 문제 • 연속된리스트 원소를 제거하는 compress 함수를 작성하시오 • compress :: Eq a => [a] -> [a] > compress "aaaabccaadeeee" "abcade"
  • 92.
    연습 문제 • 리스트를run-length 인코딩으로 리턴하는 함수 encode를 작성하시오 • encode :: Eq a => [a] -> [(Int, a)] > encode "aaaabccaadeeee" [(4,'a'),(1,'b'),(2,'c'),(2,'a'),(1,'d'),(4,'e')]
  • 93.
    숙제 • CIS 194Homework 1,2,3,4,5번을 풉니다 • http://www.seas.upenn.edu/~cis194/lectures.html