#title Erlang 예제정리 [[TableOfContents]] ==== for ==== {{{ -module(lib_misc). -export([for/3]). for(Max, Max, F) -> [F(Max)]; for(I, Max, F) -> [F(I)|for(I+1, Max, F)]. }}} ==== 리스트의 요소 개수는? ==== {{{ -module(list_count). -export([count/1]). count([H|T]) -> 1 + count(T); count([]) -> 0. }}} L = [1,2,3] 이라면.. 1. 1 + count([2,3]) 1. 1 + 1 + count([3]) 1. 1 + 1 + 1 + count([]) 1. 1 + 1 + 1 + 0 ==== Tuple의 요소 개수 ==== exam01.erl {{{ -module(exam01). -export([count/1]). count(Tuple) -> count(tuple_to_list(Tuple), 0). count([H|T], N) -> count(T, N+1); count([], N) -> N. }}} {{{ 1> c(exam01). exam01.erl:5: Warning: variable 'H' is unused {ok,exam01} 2> X = {1,2,{3,4}}. {1,2,{3,4}} 3> exam01:count(X). 3 }}} tuple_size()를 이용하면 쉽게 투플의 요소 개수를 구할 수 있다. {{{ 4> tuple_size(X). 3 5> }}} 음.. 조금 아쉽다. 리스트 요소의 개수는 1. {1} 2. {2} 3. {3,4} 로 3개다. 3번째 요소도 리스트이므로 이것까지 몇 개인지 알고 싶다. 다음과 같이 해봤다. exam02.erl {{{ -module(exam02). -export([loop/1]). loop(Tuple) -> loop(Tuple, lists:seq(1, tuple_size(Tuple))). loop(Tuple, [H|T]) -> L = to_list(H, Tuple), loop(Tuple, T, L). loop(Tuple, [H|T], L) -> L1 = to_list(H, Tuple), loop(Tuple, T, L ++ L1); loop(Tuple, [], L) -> length(L). to_list(H, Tuple) -> case is_tuple(element(H,Tuple)) of true -> tuple_to_list(element(H,Tuple)); false -> [element(H,Tuple)] end. }}} {{{ 1> c(exam02). exam02.erl:13: Warning: variable 'Tuple' is unused {ok,exam02} 2> X = {1,2,{3,4}}. {1,2,{3,4}} 3> exam02:loop(X). 4 4> }}} 함수의 내용이 좀 꾸졌다. 그래서 다음과 같이 바꿔봤다. {{{ -module(exam03). -export([count/1]). count(Tuple) -> count(tuple_to_list(Tuple), 0). count([H|T], N) -> case is_tuple(H) of true -> count(T, N + tuple_size(H)); false -> count(T, N + 1) end; count([], N) -> N. }}} {{{ 1> c(exam03). {ok,exam03} 2> X = {1,2,{3,4}}. {1,2,{3,4}} 3> exam03:count(X). 4 4> }}} ==== 리스트 해석(list comprehension) ==== {{{ %%리스트에 2 곱하기 L1=[1,2,3,4,5]. [2*X || X <- L1]. %%튜플의 요소끼리 곱하기 L2 = [{1,3}, {2,3}]. [A*B || {A,B} <- L2]. %%{a, X} 형태(X값은 어떤 값이든지 상관없다)만 filter L3= [{a,1}, {b,1}, {c,1}, {a,2}, {b,2}, {c,2}]. [X || {a, X} <- L3]. [{a,X} || {a, X} <- L3]. %%리스트 L4에서 3보다 큰 리스트 L4 = [1,2,3,4,5]. [X || X <- L4, X > 3]. %%리스트 병합 [1,2,3] ++ [4,5,6]. %%여러 조건, 여러 리스트 [{A, B} || A <- lists:seq(1,10), B <- lists:seq(11, 20), A rem 2 =:= 0, B rem 2 =:= 0]. %%Catesian Product [[X]++[Y] || X<-"HT", Y<-"HT"]. }}} {{{ erlang@A:~/work$ erl Erlang (BEAM) emulator version 5.6.5 [source] [smp:4] [async-threads:0] [kernel-poll:false] Eshell V5.6.5 (abort with ^G) 1> L1=[1,2,3,4,5]. [1,2,3,4,5] 2> [2*X || X <- L1]. [2,4,6,8,10] 3> 3> L2 = [{1,3}, {2,3}]. [{1,3},{2,3}] 4> [A*B || {A,B} <- L2]. [3,6] 5> 5> L3= [{a,1}, {b,1}, {c,1}, {a,2}, {b,2}, {c,2}]. [{a,1},{b,1},{c,1},{a,2},{b,2},{c,2}] 6> [X || {a, X} <- L3]. [1,2] 7> [{a,X} || {a, X} <- L3]. [{a,1},{a,2}] 8> 8> L4 = [1,2,3,4,5]. [1,2,3,4,5] 9> [X || X <- L4, X > 3]. [4,5] 10> 10> [1,2,3] ++ [4,5,6]. [1,2,3,4,5,6] 10> 11> [{A, B} || A <- lists:seq(1,10), B <- lists:seq(11, 20), A rem 2 =:= 0, B rem 2 =:= 0]. [{2,12}, {2,14}, {2,16}, {2,18}, {2,20}, {4,12}, {4,14}, {4,16}, {4,18}, {4,20}, {6,12}, {6,14}, {6,16}, {6,18}, {6,20}, {8,12}, {8,14}, {8,16}, {8,18}, {8,20}, {10,12}, {10,14}, {10,16}, {10,18}, {10,20}] 12> 12> [[X]++[Y] || X<-"HT", Y<-"HT"]. ["HH","HT","TH","TT"] }}} ==== quick sort ==== {{{ -module(sort). -export([qsort/1]). qsort([Pivot|T]) -> qsort([X || X <- T, X < Pivot]) ++ [Pivot] ++ qsort([X || X <- T, X >= Pivot]); qsort([]) -> []. }}} {{{ erlang@A:~/work$ erl Erlang (BEAM) emulator version 5.6.5 [source] [async-threads:0] [kernel-poll:false] Eshell V5.6.5 (abort with ^G) 1> c(sort). {ok,sort} 2> sort:qsort([3,8,7,1,5]). [1,3,5,7,8] 3> }}} 리스트 [3,8,7,1,5]에 대하여.. 1. Pivot은 리스트의 헤더인 3, 3을 중심으로 왼쪽은 작은값, 오른쪽은 크거나 같은 값으로 정렬하면 된다. 1. 3보다 작은 값 [1] ++ [3] ++ [8, 7, 5] 1. 리스트 [1]과 [3]은 헤더만 있으므로 qsort([]) -> []를 만나 종료되고, [8, 7, 5]에서 8이 Pivot이 되어 [7, 5] ++ [8] ++ [], 중간결과는 [1] ++ [3] ++ [7, 5] ++ [8] 1. 리스트 [7, 5]만 돌면 됨. Pivot값이 7이 되어 7보다 작은 [5] ++ [7] ++ []. 1. 최종결과는 [1] ++ [3] ++ [5] ++ [7] ++ [8] ==== Permutations ==== {{{ 1> L = [1,2,3]. [1,2,3] 2> [[X]++[Y] || X<-L,Y<-L]. [[1,1],[1,2],[1,3],[2,1],[2,2],[2,3],[3,1],[3,2],[3,3]] 3> [[X]++[Y] || X<-L,Y<-L--[X]]. [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]] 4> }}} {{{[[X]++[Y] || X<-L,Y<-L--[X]]}}}에 대한 설명(여기서 {{{*}}}는 Catesian Product) * [[X]++[Y] || X<-L,Y<-L]는 L * L 즉, L과 L의 Catesian Product * Y<-L 대신 Y<-L--[X]이면, X<-L이므로 리스트X의 각각의 요소에 대해서 * [1,2,3] -- [1] = [2,3] * [1,2,3] -- [2] = [1,3] * [1,2,3] -- [3] = [1,2] * 결과는.. * 1 * [2,3] = [1,2], [1,3] * 2 * [1,3] = [2,1], [2,3] * 3 * [1,2] = [3,1], [3,2] * [[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]] 대괄호의 추가로 다음과 같이 만들 수 있다. {{{ 1> L = [1,2,3]. [1,2,3] 2> [[H|T] || H<-L, T<-[L--[H]]]. [[1,2,3],[2,1,3],[3,1,2]] 3> }}} * {{{[1|[2,3]] = [1|[1,2,3]--[1]]}}} * {{{[2|[1,3]] = [2|[1,2,3]--[2]]}}} * {{{[3|[1,2]] = [3|[1,2,3]--[3]]}}} 다음은 또 다른 예제.. {{{ > [{X, Y} || X <- [1,2,3], Y <- [a,b]]. [{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}] }}} {{{ -module(perms). -export([perms/1]). perms([]) -> [[]]; perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])]. %%ㅎㅎ 이거 동작을 이해하는데 3일 걸린거 같아요.. 짬짬히 생각해서 그런가?? ㅋ }}} {{{ 1> c(perms). {ok,perms} 2> perms:perms([1,2,3]). [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] }}} 1. {{{[1|[2,3]] = [1|[1,2,3]--[1]]}}} = [1,2,3] ... (1) 1.1 (1)에대해서 재귀호출 1.1 [2,3] -- [2] = [3] 1.1 [2,3] -- [3] = [2] 1.1 [1|[3,2]] = [1,3,2] 1. {{{[2|[1,3]] = [2|[1,2,3]--[2]]}}} = [2,1,3] ... (2) 1.1 (2)에대해서 재귀호출 1.1 [1,3] -- [1] = [3] 1.1 [1,3] -- [3] = [1] 1.1 [2|[3,1]] = [2,3,1] 1. {{{[3|[1,2]] = [3|[1,2,3]--[3]]}}} = [3,1,2] ... (3) 1.1 (3)에대해서 재귀호출 1.1 [1,2] -- [1] = [2] 1.1 [1,2] -- [2] = [1] 1.1 [3|[2,1]] 1. 결국.. [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 참고자료 * http://www.erlang.org/doc/programming_examples/list_comprehensions.html (Generators can be combined. For example, the __Cartesian product of two lists__ can be written as follows: ...) * http://en.wikibooks.org/wiki/Erlang_Programming/List_Comprehensions ==== Map ==== {{{ -module(mymap). -export([map/2]). map(_,[]) -> []; map(F,[H|T]) -> [F(H)|map(F,T)]. }}} F는 fun이고, L = [1,2,3]이라면.. (참고: {{{[1|[2|[3]]] = [1,2,3]}}} 즉, 같은 패턴이다) 1. {{{[F(1)|map(F,[2,3])]}}} 1. {{{[F(1)|[F(2)|map(F,[3])]]}}} 1. {{{[F(1)|[F(2)|[F(3)|map(F,[])]]]}}} {{{ erlang@A:~/work$ erl Erlang (BEAM) emulator version 5.6.5 [source] [async-threads:0] [kernel-poll:false] Eshell V5.6.5 (abort with ^G) 1> c(mymap). {ok,mymap} 2> F = fun(X) -> X * X end. #Fun 3> L = [1,2,3]. [1,2,3] 4> mymap:map(F,L). [1,4,9] 5> }}} ==== factorial ==== {{{ -module(mymath). -export([factorial/1]). factorial(1) -> 1; factorial(N) -> N * factorial(N-1). }}} N = 4 이면, 1. '''4 * factorial(4-1)''' 1. '''4 * 3 * factorial(3-1) ===> 단계1의 factorial(4-1)의 결과는 "3 * factorial(3-1)"이므로 ''' 1. '''4 * 3 * 2 * factorial(2-1) ===> 단계2의 factorial(3-1)의 결과는 "2 * factorial(2-1)"이므로 ''' 1. '''4 * 3 * 2 * 1 ===> 단계3의 factorial(2-1)의 결과는 "1" 이므로 ''' 10000!(facotrial(10000))이 약 1초 걸린다. 그것도 VMWare의 이 꼬진 노트북에서..ㅋㅋ ==== case와 if ==== {{{ -module(misc). -export([filter/2, max/2, filter2/2, acc/1, acc/3]). filter(P, [H|T]) -> case P(H) of true -> [H|filter(P, T)]; false -> filter(P,T) end; filter(P,[]) -> []. max(X,Y) -> Z = if %% 여기를 주목!! if의 결과가 Z로 바운드 된다. X< Y -> Y; true -> X end, %% 세미콜론(;)이 아닌 콤마(,)다!!!!! Z. filter2(P, [H|T]) -> ReturnVal = P(H), if ReturnVal =:= true -> [H|filter2(P,T)]; ReturnVal =:= false -> filter2(P,T) end; filter2(P, []) -> []. %%리스트 L을 한 번만 읽어서 2개의 리스트 만들기 acc(L) -> acc(L, [], []). acc([H|T], Odds, Evens) -> case (H rem 2) of 1 -> acc(T, [H|Odds], Evens); 0 -> acc(T, Odds, [H|Evens]) end; acc([], Odds, Evens) -> {lists:reverse(Odds), lists:reverse(Evens)}. }}} {{{ 5> L = [1,2,3,4,5,6,7,8,9]. * 2: syntax error before: 2 5> L = [1,2,3,4,5,6,7,8,9]. [1,2,3,4,5,6,7,8,9] 6> P = fun(X) -> X rem 2 =:= 0 end. #Fun 7> misc:filter(P,L). [2,4,6,8] 8> misc:max(2,3). 3 9> misc:filter2(P,L). [2,4,6,8] 10> misc:acc(L). {[1,3,5,7,9],[2,4,6,8]} }}} ==== try ... catch ==== {{{ -module(try_test). -export([demo1/0, demo2/0, demo3/0]). generate_exception(1) -> a; generate_exception(2) -> throw(a); generate_exception(3) -> exit(a); generate_exception(4) -> {'EXIT', a}; generate_exception(5) -> erlang:error(a). demo1() -> [catcher(I) || I <- [1,2,3,4,5]]. demo2() -> [{I, (catch generate_exception(I))} || I <- [1,2,3,4,5]]. demo3() -> try generate_exception(5) catch error:X -> {X, erlang:get_stacktrace()} end. catcher(N) -> try generate_exception(N) of Val -> {N, normal, Val} catch throw:X -> {N, caught, throw, X}; exit:X -> {N, caught, exited, X}; error:X -> {N, caught, error, X} end. }}}