SlideShare a Scribd company logo
1 of 52
SW공학연구소
한주영
개미수열 프로그래밍
look-and-say sequence
개미수열
1
11
21
1211
111221
312211
13112221
1113213211
?? n번째 줄 출력하기
개미수열은 앞 줄을 읽어 다음 줄
을 만들어내는 수열
두 번째 줄 “11”을 “2개의 1(영어
식 표현)”이라고 읽어서 “21”이 됨
?? 로 표시한 9번째 줄은 8번째
줄을 읽어서 구할 수 있음
“3개의 1, 1개의 3, 1개의 2, …”
 “31131211131221”
문제: 개미수열의 n(0 기준)번째
줄을 출력하는 함수를 작성하라
열 명 중 아홉 명은…
String ant(int n) {
String s = "1";
for (int i = 0; i < n; i++) {
char c = s.charAt(0);
int count = 1;
String result = "";
for (int i = 1; i < s.length(); i++) {
if (c == s.charAt(i)) count++;
else {
result += count;
result += c;
c = s.charAt(i);
count = 1;
}
}
result += count;
result += c;
s = result;
}
return s;
}
대부분 중첩 for로 작성
s는 “1”로 시작하고,
n만큼 반복 적용
s를 읽으면서 이미 읽은 글자 c와
비교하고 같으면 count++
다르면 다음 줄 결과 result에
count와 c를 append
아주 일부는
String ant(int n) {
String s = "1";
for (int i = 0; i < n; i++) {
s = next(s);
}
return s;
}
String next(String s) {
char c = s.charAt(0);
int count = 1;
String result = "";
for (int i = 1; i < s.length(); i++) {
if (c == s.charAt(i)) count++;
else {
result += count;
result += c;
c = s.charAt(i);
count = 1;
}
}
result += count;
result += c;
return result;
}
next() 함수를 만들고 반복 적용하
는 식으로 작성하는 경우도 있음.
한 덩어리로 작성하는 것보다는
낫다.
오늘 우리는
• 다양한 방법으로 개미수열을 풀어봅니다
JavaScript/Regex
• 규칙을 Regular Expression으로 표현할 수 있다면 매우 다행
– 실제로 그렇지 못한 경우가 많음
– 게다가, 정확한 Regex를 작성하는 것은 매우 어려운 일
function next(s) {
return s.replace(/(.)1*/g, g => g.length + g[0])
}
“1112…”
3 1
g
length g[0]
Java/Regex
• Java에도 JavaScript의 replace같은 함수가 있다면…
– 직접 만들면 됨
String next(String s) {
return replaceAll(s, "(.)1*", g -> format("%d%c", g.length(), g.charAt(0)));
}
String replaceAll(String s, String regex, UnaryOperator<String> f) {
StringBuffer sb = new StringBuffer();
Matcher m = Pattern.compile(regex).matcher(s);
while (m.find()) {
String g = m.group();
m.appendReplacement(sb, f.apply(g));
}
m.appendTail(sb);
return sb.toString();
}
기본 틀은 compile/matcher/find/group
추가로, appendReplacement와
appendTail을 이용하면 JavaScript의
replace처럼 동작하는 도움함수를 작성
할 수 있음
Java8의 UnaryOperator를 이용
App-Specific vs. General
• 처음 ant(n)는 한 덩어리
• next(s)만 분리되어도 좀 나음
• next(s)도 Regex로 한단계 더 분리되었음
– replaceAll(s,regex,f)은 일반적인 함수
ant(n)
next(s)
replaceAll(s, regex, f)
문제를 잘게 나누다보면 결국 일반적인 작은 문
제들이 되고,
이런 작은 문제들은 빈번하게 등장하게 된다.
replaceAll(s,regex,f)는 어디나 써먹을 수 있음
next() 함수를 더 나누기
• 열명 중 아홉이 짜는 next()
String next(String s) {
char c = s.charAt(0);
int count = 1;
String result = "";
for (int i = 1; i < s.length(); i++) {
if (c == s.charAt(i)) count++;
else {
result += count;
result += c;
c = s.charAt(i);
count = 1;
}
}
result += count;
result += c;
return result;
}
count
compare
loop
format/append
next는 리스트 프로세싱
• List<Integer> next(List<Integer> ns)
– 리스트를 통째로 다루기
13112221
1 3 11 222 1
11 13 21 32 11
1113213211
32
List<List<A>> group(List<A> as)
List<B> map(Function<A,B> f,
List<A> as)
List<A> concat(List<List<A>> ass)
다른 접근을 살펴보기 위해 String 대
신 List<Integer>로 바꿔보자!
리스트를 처리하는
group/map/concat 도움함수는
signature만 보더라도 매우 일반화된
함수라는 것을 알 수 있음
next()는 리스트 프로세싱
List<Integer> next(List<Integer> ns) {
return concat(map(g => listOf(g.size(), g.get(0)), group(ns));
}
ant(n)
next(s)
group (list)
map(f, list)
concat(list-of-list)
String에서 replaceAll같은 일반화된 도움함수를 사용
한 것처럼 List에 대해 일반화된 도움함수를 얻을 수 있
고, 이를 이용하면 next()는 아주 간단히 해결됨
for/==/++/+= 등의 primitive는 감춰지기 때문에 실수할
여지가 줄어듬
Recap
• ant/next
– for/count/==/+=/…
• next/regex
– replace(regex, f)
• next/list-processing
– concat/map/group
개미수열 n == 100
1
11
21
1211
111221
312211
13112221
1113213211
..
..
..
...
?? 100 번째 줄 출력하기
개미수열
OutOfMemoryError
• 한 줄마다 길이가 30%씩 증가
– 100번째 줄의 길이는??
– 대충… 5천억 = 5e11 = 500G
• 그럼 어떻게?
https://en.wikipedia.org/wiki/Look-
and-say_ sequence
이미 이 수열의 각 줄이 30%씩 증가
한다는 것을 증명한 수학자가 있음
100번째 줄은 String/List 에 담을 수
도 없다.
출력하기 위해 꼭 String/List에 담아
둘 필요는 없음
Iterator
• 무한 수열을 나타낼 수 있음
– boolean hasNext() { return true; }
Iterator<Integer> ant(int n) {
Iterator<Integer> s = asList(1).iterator();
for (int i=0; i<n; i++)
s = new Next(s);
return s;
}
class Next implements Iterator<Integer> { ... }
Iterator
• 100번째 줄 위로는 필요한 만
큼만 계산
– lazy evaluation
• 메모리 문제는 없지만 5천억개가
출력될 때까지 지켜봐야 함 
1
Next
Next
Next
Next
Next while (s.hasNext())
System.out.print(s.next());
Next는 또다른 Iterator를 포함하
는 Wrapper Iterator
개미수열 n == 100
1113122113121113222123211211131211121311
1213211231132132211211131221232112111312
2112131112131221121321132132211231131122
2113311213212322211211131221131211221321
1231132132211211131221131211132221121311
1213122112132113121113222112132113213221
1331121321232221123113112221131112311322
3112111311222112132113311213211221121332
2112111312211312111322212311222122132113
2132211231131122211331121321232221121113
1221131211132221232112111312111213322112
1311121312211213211312111322211213211321
32212321121113121112…
class Next implements Iterator<Integer> {
public Integer next() {
if (state == State.INIT) {
state = State.HAS_NEXT;
next = inner.next();
}
if (state == State.HAS_NEXT) {
state = State.LAST;
elem = next;
count = 1;
while (inner.hasNext()) {
int next = inner.next();
if (next == elem) {
count++;
} else {
state = State.COUNT;
this.next = next;
break;
}
}
return count;
} else if (state == State.LAST) {
state = State.INIT;
return elem;
} else {
state = State.HAS_NEXT;
return elem;
}
}
Next 검토
• 그런데, 다시 처음 그 문제
가…
– 한 덩어리 Next.next()
• 그건 그렇고, 왜 for문보다
더 복잡하지?
– loop를 while(hasNext())로
넘겼음
– 상태변수를 따로 두어야 함
– 연속해서 값을 생성하기 어
려움
리스트 프로세싱
• 한 덩어리 문제는 해결됨
• 복잡해진 진짜 원인은 loop를 빼앗긴 탓
– Iterator는 Loop 컨트롤을 외부로 빼앗겼음
– 상태 유지가 더 힘들어졌음
Iterator<Integer> next(Iterator<Integer> s) {
return new Concat(new Map(g -> …, (new Group(s)));
}
Concat/Map/Group은 Decorator
Map/Group을 합쳐서 RunLength 이터레이터를 만들 수도 있음
Concat/Map을 합쳐서 ConcatMap 이터레이터를 만들어도 됨
JavaScript/Generator
• 상태를 가지는
Iterator를 쉽게 만들
수 있는 도구
function *next(line) {
let prev = line.next().value
let count = 1
for (let c of line) {
if (prev === c)
count++
else {
yield count; yield prev
prev = c
count = 1
}
}
yield count; yield prev
}
for를 가지고
있다!
loop 내에서 여
러 값을 출력할
수도
Java/Generator
• Generator는
– yield에서 control을 놓고
– next()호출하면 resume
• 일종의 Coroutine
– Java에서는 Thread로 Generator Coroutine을 구현할 수 있
음
Generator<Integer> ints = Generator.of((g) -> {
int i = 0;
while (true)
g.yield(i++);
});
for (int i = 0; i < 100; i++) {
System.out.println(ints.next());
}
Thread로 구현하면 Thread를 종료시켜
줘야 하는 문제가…
일단 Generator interface는 쉽게 구현
할 수 있음.
JavaScript의 Generator처럼 사용 가능
• 코루틴은 서브루틴보다 더 일반적인 개념.
• 서브루틴은 call/return 뿐이지만, 코루틴은 suspend/resume이 가능.
• 코루틴이 suspend하지 않으면 그것이 서브루틴
• 이런의미로 앞의 Generator는 일종의 코루틴 (코루틴은 다른 코루틴을 suspend하면서 다른
코루틴을 resume할 수있는데, Generator는 suspend하면 호출한 쪽으로 되돌아감)
• Iterator/infinite list를 만드는데 사용할 수 있다고 나와 있음
Go/goroutine
• Go 언어는 고루틴/채널을 기본 제공
• go f()
– f()함수를 새로운 고루틴에서 실행
• c := make(chan int)
– 고루틴 간의 통신은 채널을 이용
– c  0 : c로 값을 전달
–  c : c에서 값을 읽음
Go/goroutine
1
Next
Next
Next
Next
Next
고루틴
채널
ch := make(chan int)
go func() {
ch <- 1
close(ch)
}()
for i := 0; i < n; i++ {
ch1 := make(chan int)
go next(ch, ch1)
ch = ch1
}
func next(in, out chan int) {
... generator와 거의 같음
}
채널 연산은 yield/resume 역할을 함
Go에서 고루틴을 이용하여 작성하면 n == 100을 쉽
게 출력할 수 있음
Java/goroutine
• Thread/BlockingQueue를 이용하여 go/send/recv/close 만들기
interface Goroutine {
void run()
}
static void go(Goroutine go) {
new Thread(() -> go.run()).start();
}
class Chan<A> {
SynchronousQueue<Option<A>> queue = new …
void send(A a) { queue.put(option(a)); }
Option<A> recv() { return queue.take(); }
void close() { queue.put(option()); }
}
Chan<Integer> ch = new .
go(() -> {
ch.send(1);
ch.close();
})
Recap
• Iterator
– next()
• Generator
– next()/yield
• Coroutine
– with Thread/goroutine
n == 100에서 어떻게  Iterator로 변형
Iterator 구현이 지저분함
 JavaScript의 Generator  Java로 …
 Go의 Goroutine  Java로 …
개미수열 n == 10000
1
11
21
1211
111221
312211
13112221
1113213211
..
..
..
..
..
..
..
..
..
..
..
..
..
.. ..
..
..
..
..
..
??
10000 번째 줄 출력하기
개미수열
StackOverflowError
• Iterator.next -> next -> next …
– Generator는 producer 코루틴을 위한 것
– Next는 transducer 코루틴(출력 뿐 아니라 입력도 필요하다)
• Thread를 이용한 Coroutine의 경우에는 OutOfMemoryError
– OutOfMemoryError: unable to create new native thread
• Go는 괜찮은데..
– Goroutine은 Green thread
• 그럼 어떻게?
– Go의 Green thread를 흉내내거나
– Stack을 사용하지 않는 Coroutine을 만들거나
개미수열 복잡도
50번째 줄을 출력하는 과정 살펴보기
맨 아랫줄에 빨간색 칸은 그 위치의 값을 계산하려
고 suspen되었음을 보여주고
그 앞줄/그 앞줄… 도 suspend
그러다 값이 계산되면 차례로
resume/resume/resume될 것
어느 한 순간에는 하나의 Coroutine만 Active!
Coroutine의 본질
• Cooperative multitasking (non-preemptive)
– yield/resume
– 각각을 쓰레드로 보더라도 동시에 실행되지는 않음
• producer  transducer  consumer
start
yield
데이터 요청
resume
yield
데이터 요청
resume
yield
데이터 생성
resume
yield
데이터 요청
resume
yield
데이터 생성
yield
데이터 생성
resume
startstart
Go에서는 채널에
데이터 요청/전달
나머진 거의 같음
• 첫줄(1을 출력)은 Producer,
• 앞줄을 읽어 다음 줄을 생성하는
Next()는 Transducer,
• n번째 줄을 출력하는 건
Consumer임
코루틴이 yield하는 이유가 두가지
(요청/생성)
resume/yield
• 우리가 가진 건 서브루틴 뿐
– call/return
• return할 내용
– yield하는 이유(값 요청? 전달?)
– 다시 resume할 위치
• call할 때 전달할 내용
– resume할 위치
– 요청 값
• 코루틴들을 organize할 dispatcher 함수 필요
– 각 코루틴들의 상태를 기억(스택 변수는 쓸모없음)
• 심지어 C로도 가능하다
C/Coroutine
typedef struct state {
char prev; // 이전에 읽은 값
char count; // 현재까지 누적 카운트
char next; // 다음으로 읽은 값
char ptr; // resume할 위치
} state;
int init(state *s) {
switch (s->ptr) {
case 0:
s->ptr = 1;
return 1;
default:
return 0;
}
}
반환값 약속
• -1: 값 요청
• 0: 스트림 종료
• 1/2/3: 값 전달
func init(i, o) {
o <- 1
close(o)
}
go init(ch)
function *init() {
yield 1
return
}
resume 위치를 반환하
는 방법도 있음
C/Coroutine
typedef struct state {
char prev; // 이전에 읽은 값
char count; // 현재까지 누적 카운트
char next; // 다음으로 읽은 값
char ptr; // resume할 위치
} state;
int init(state *s) {
switch (s->ptr) {
case 0:
s->ptr = 1;
return 1;
default:
return 0;
}
}
int next(state *s) {
switch (s->ptr) {
case 0:
s->ptr = 1;
return -1;
case 1:
s->prev = s->next;
s->count = 1;
s->ptr = 2;
return -1;
case 2:
if (s->prev == s->next) {
s->count++;
return -1;
} else if (s->next == 0) {
s->ptr = 3;
return s->count;
반환값 약속
• -1: 값 요청
• 0: 스트림 종료
• 1/2/3: 값 전달
값을 읽기 위해
yield
ptr 조작 X
loop!!
resume
resume
다음 값을 읽음
yield
C/Coroutine
int n = 1000000;
state* lines = (state*)calloc(n + 1, sizeof(state));
int cur = n + 1;
while (1) {
int result = (cur == 0) ? init(&lines[0]) : next(&lines[cur]);
switch (result) {
case -1: // read
cur--;
break;
default: // close or write 1/2/3
if (cur < n) {
cur++;
lines[cur].next = result;
} else {
printf("%d", result);
}
}
값을 읽으려면 선행 코루틴
실행해야
다음 코루틴으로 값을 전달하고
resume
개미수열 복잡도
• 공간 복잡도
– O(n)
• 시간 복잡도
– n번째 줄 m번째 글자까지 출력
– O(n + m log m)
C/Coroutine 검토
• No abstraction
– 중복 코드를 제거할 수도 없다
– 함수를 분리할 수도 없다
Coroutine vs. Continuation
• Coroutine
– Thread 이용
• 실제로 pause/resume
– resume pointer
• Continuation을 반환하는 것으로 이해할 수 있음
• Continuation
– ptr 반환후 resume할 때 jump 하는 대신
– 다음 실행할 continuation을 closure로 반환
– resume은 continuation을 호출하는 것
JavaScript/CPS
• Continuation Passing Style
– setTimeout(continuation, 1000)
• 1초뒤 실행할 내용을 continuation에 담아 전달
function init() {
return write(1, undefined)
}
function write(value, cont) {
return { type: 'write', cont }
}
1을 전달하고 다음 실행할
내용은 없다
JavaScript/CPS
function init() {
return write(1, undefined)
}
function next() {
return read(c => loop(c, 1))
function loop(prev, count) {
return read(c => {
if (typeof c === 'undefined') return write(count, () => write(prev, undefined))
else if (prev === c) return loop(prev, count + 1)
else return write(count, () => write(prev, () => loop(c, 1)))
})
}
}
첫 글자 읽고 loop 진입
loop에서
글자 읽어서
종료? count/prev 출력 후 종료
같음? count증가 후 loop 반복
다름? count/prev 출력 후 새로 읽은 글자로 반복
JS/CPS 검토
• write2 같은 추상화 가능
• Callback Hell
– 흐름을 추적하기 어려움
– Promise같은 CPS 추상화 필요
function write2(a, b, cont) {
return write(a, () => write(b, cont))
}
JS/Promise
• Callback에 대한 추상화
– 직접 Callback 인자를 받고, Callback을 호출하는 대신
– Callback을 처리할 Promise 객체를 반환
– Promise가 Callback을 처리
– Chaining이 가능한 then 메쏘드
step1(arg1, (res1) => {
step2(arg2, (res2) => {
step3(arg3, (res3) => {
...
})
})
})
step1(arg1)
.then((res1) => ... step2(arg2))
.then((res2) => ... step3(arg3))
.then((res3) => ...)
Read/Write 추상화
class Read {
constructor(cont) { this.cont = cont }
then(f) {
return new Read(x => this.cont(x).then(f)
}
}
class Write {
constructor(value, cont) { this.value = value; this.cont = cont }
then(f) {
return new Write(this.value, this.cont.then(f))
}
}
function read() { return new Read(undefined) }
function write(value) { return new Write(value, undefined) }
Read/Write 추상화
function init() {
return write(1)
}
function next() {
return read()
.then(c => loop(c, 1))
function loop(prev, count) {
return read()
.then(c => {
if (typeof c === 'undefined') return write2(count,prev)
else if (prev === c) return loop(prev, count + 1)
else return write2(count, prev)
.then(() => loop(c, 1))
})
}
}
CPS 추상화 검토
• 추상화를 깔고 새로운 추상화
• OOP Design Patterns의 Interpreter패턴
– Read/Write 는 cont를 가지며 Composite
function forever(program) {
return program.then(() => program)
}
const echo = read().then(write)
const prog = forever(echo)
run(prog)
Recap
• hand-written Coroutine
• JS/Continuation passing style
• CPS 추상화
무한 수열
• 더 일반적인 방법
– Stream/Lazy list
Scala/Haskell
• Scala는 Stream[A] 라는 Lazy list를 지원
• Haskell은 기본 리스트가 Lazy (사실 전부 lazy)
def ant = Stream.iterate(Stream(1))(next)
def next(s: Stream[Int]) = group(s) flatMap {g => Stream(g.size, g.head)}
def group[A](as: Stream[A]): Stream[Seq[A]] = ...
ant(1000000)(1000000) // 1M번째 줄 1M번째 글자
ant = iterate(group >=> sequence[length, head]) [1]
Java/JS
• Java와 JS는 동적 언어
• 쉽게 Lazy list를 만들 수 있다.
• Lazy list의 핵심은 Linked list의 Tail을 필요할 때 생성하기
class List<A> {
...
public List(A head, Supplier<List<A>> tail) { ...}
public A head() { return head; }
public List<A> tail() { return tail.get(); }
}
List<Integer> intsFrom(int n) {
return new Node(n, () => intFrom(n+1))
}
Java/JS
• Java와 JS는 동적 언어
• 쉽게 Lazy list를 만들 수 있다.
• Lazy list의 핵심은 Linked list의 Tail을 필요할 때 생성하기
class List<A> {
...
public List(A head, Supplier<List<A>> tail) { ...}
public A head() { return head; }
public List<A> tail() { return tail.get(); }
}
List<Integer> intsFrom(int n) {
return new Node(n, () => intFrom(n+1))
}
List vs. Stream
• List/Stream은 같은 추상화의 동작만 다
른 형태
List<Integer> next(List<Integer> ns) {
return concat(map(g => listOf(g.size(), g.get(0)), group(ns));
}
Stream<Integer> next(Stream<Integer> ns) {
return concat(map(g => streamOf(g.size(), g.head()), group(ns));
}
Recap
• for-loop, regex
• list processing
– stream(lazy list)도 마찬가지
• iterator/generator/coroutine
– Thread로 흉내내기
– Coroutine의 본질 – resume ptr
• Continuation passing
– CPS 추상화 : Interperter 패턴
감사합니다.
• http://github.com/jooyunghan/look-and-say

More Related Content

What's hot

TOR penyuluhan integritas dan akuntabilitas aparatur desa
TOR penyuluhan integritas dan akuntabilitas aparatur desaTOR penyuluhan integritas dan akuntabilitas aparatur desa
TOR penyuluhan integritas dan akuntabilitas aparatur desaMAHMUN SYARIF
 
Rancangan dilla aprilya muchtar paramedis
Rancangan dilla aprilya muchtar paramedisRancangan dilla aprilya muchtar paramedis
Rancangan dilla aprilya muchtar paramedistemanna #LABEDDU
 
Contoh KPI Individu. Rapor Prestasi Kerja
Contoh KPI Individu. Rapor Prestasi KerjaContoh KPI Individu. Rapor Prestasi Kerja
Contoh KPI Individu. Rapor Prestasi KerjaBusinessBuddy Int
 
Konsep-Manajemen-Keperawatan-ppt.ppt
Konsep-Manajemen-Keperawatan-ppt.pptKonsep-Manajemen-Keperawatan-ppt.ppt
Konsep-Manajemen-Keperawatan-ppt.pptRetno Lusmiati Anisah
 
Indikator kinerja rs
Indikator kinerja rsIndikator kinerja rs
Indikator kinerja rsResdi Budaya
 
Tugas kelompok 3 motorik
Tugas kelompok 3 motorikTugas kelompok 3 motorik
Tugas kelompok 3 motorikporja_b
 
PAKET PENDAMPINGAN KLINIK 2023,ryn.pptx
PAKET PENDAMPINGAN KLINIK 2023,ryn.pptxPAKET PENDAMPINGAN KLINIK 2023,ryn.pptx
PAKET PENDAMPINGAN KLINIK 2023,ryn.pptxSuliHarto1
 
format pengkajian keperawatan komunitas
format pengkajian keperawatan komunitasformat pengkajian keperawatan komunitas
format pengkajian keperawatan komunitasLSIM
 
Konsep dasar manajemen keperawatan
Konsep dasar manajemen keperawatanKonsep dasar manajemen keperawatan
Konsep dasar manajemen keperawatanZulfikar Muhammad
 
Manajemen kinerja (pendekatan dalam penilaian kinerja & Pengukuran ekonomi, e...
Manajemen kinerja (pendekatan dalam penilaian kinerja & Pengukuran ekonomi, e...Manajemen kinerja (pendekatan dalam penilaian kinerja & Pengukuran ekonomi, e...
Manajemen kinerja (pendekatan dalam penilaian kinerja & Pengukuran ekonomi, e...Ganesha Aulia
 
Presentation 5 (pengembangan karir)
Presentation 5 (pengembangan karir)Presentation 5 (pengembangan karir)
Presentation 5 (pengembangan karir)RonnyAja
 
Pelayanan jamkesmas
Pelayanan jamkesmasPelayanan jamkesmas
Pelayanan jamkesmasJoni Iswanto
 
Handout Training-CUSTOMER SERVICE-Sinar Himalaya-Technician
Handout Training-CUSTOMER SERVICE-Sinar Himalaya-TechnicianHandout Training-CUSTOMER SERVICE-Sinar Himalaya-Technician
Handout Training-CUSTOMER SERVICE-Sinar Himalaya-TechnicianAnton Lunardi
 
Disiplin belajar petter m. senge
Disiplin belajar petter m. sengeDisiplin belajar petter m. senge
Disiplin belajar petter m. sengemuhammadfatih35
 
Kualitas pelayanan di Pizza Hut
Kualitas pelayanan di Pizza HutKualitas pelayanan di Pizza Hut
Kualitas pelayanan di Pizza HutVerrell Max
 

What's hot (20)

TOR penyuluhan integritas dan akuntabilitas aparatur desa
TOR penyuluhan integritas dan akuntabilitas aparatur desaTOR penyuluhan integritas dan akuntabilitas aparatur desa
TOR penyuluhan integritas dan akuntabilitas aparatur desa
 
Rancangan dilla aprilya muchtar paramedis
Rancangan dilla aprilya muchtar paramedisRancangan dilla aprilya muchtar paramedis
Rancangan dilla aprilya muchtar paramedis
 
Audit mutu
Audit mutuAudit mutu
Audit mutu
 
Contoh KPI Individu. Rapor Prestasi Kerja
Contoh KPI Individu. Rapor Prestasi KerjaContoh KPI Individu. Rapor Prestasi Kerja
Contoh KPI Individu. Rapor Prestasi Kerja
 
Konsep-Manajemen-Keperawatan-ppt.ppt
Konsep-Manajemen-Keperawatan-ppt.pptKonsep-Manajemen-Keperawatan-ppt.ppt
Konsep-Manajemen-Keperawatan-ppt.ppt
 
Indikator kinerja rs
Indikator kinerja rsIndikator kinerja rs
Indikator kinerja rs
 
Metoda tim keperawatan
Metoda tim keperawatanMetoda tim keperawatan
Metoda tim keperawatan
 
Tugas kelompok 3 motorik
Tugas kelompok 3 motorikTugas kelompok 3 motorik
Tugas kelompok 3 motorik
 
PAKET PENDAMPINGAN KLINIK 2023,ryn.pptx
PAKET PENDAMPINGAN KLINIK 2023,ryn.pptxPAKET PENDAMPINGAN KLINIK 2023,ryn.pptx
PAKET PENDAMPINGAN KLINIK 2023,ryn.pptx
 
format pengkajian keperawatan komunitas
format pengkajian keperawatan komunitasformat pengkajian keperawatan komunitas
format pengkajian keperawatan komunitas
 
Konsep dasar manajemen keperawatan
Konsep dasar manajemen keperawatanKonsep dasar manajemen keperawatan
Konsep dasar manajemen keperawatan
 
Manajemen kinerja (pendekatan dalam penilaian kinerja & Pengukuran ekonomi, e...
Manajemen kinerja (pendekatan dalam penilaian kinerja & Pengukuran ekonomi, e...Manajemen kinerja (pendekatan dalam penilaian kinerja & Pengukuran ekonomi, e...
Manajemen kinerja (pendekatan dalam penilaian kinerja & Pengukuran ekonomi, e...
 
Presentation 5 (pengembangan karir)
Presentation 5 (pengembangan karir)Presentation 5 (pengembangan karir)
Presentation 5 (pengembangan karir)
 
Pelayanan jamkesmas
Pelayanan jamkesmasPelayanan jamkesmas
Pelayanan jamkesmas
 
Spm rs
Spm rsSpm rs
Spm rs
 
Handout Training-CUSTOMER SERVICE-Sinar Himalaya-Technician
Handout Training-CUSTOMER SERVICE-Sinar Himalaya-TechnicianHandout Training-CUSTOMER SERVICE-Sinar Himalaya-Technician
Handout Training-CUSTOMER SERVICE-Sinar Himalaya-Technician
 
Disiplin belajar petter m. senge
Disiplin belajar petter m. sengeDisiplin belajar petter m. senge
Disiplin belajar petter m. senge
 
Hubungan terapeutik p k
Hubungan terapeutik p kHubungan terapeutik p k
Hubungan terapeutik p k
 
Kualitas pelayanan di Pizza Hut
Kualitas pelayanan di Pizza HutKualitas pelayanan di Pizza Hut
Kualitas pelayanan di Pizza Hut
 
Ppt sp hdr
Ppt sp hdrPpt sp hdr
Ppt sp hdr
 

Viewers also liked

하스켈학교 세미나 - Haxl
하스켈학교 세미나 - Haxl하스켈학교 세미나 - Haxl
하스켈학교 세미나 - HaxlJooyung Han
 
Continuations in scala (incomplete version)
Continuations in scala (incomplete version)Continuations in scala (incomplete version)
Continuations in scala (incomplete version)Fuqiang Wang
 
[DEVIEW 2016] 네이버의 모던 웹 라이브러리 - egjs
[DEVIEW 2016] 네이버의 모던 웹 라이브러리 - egjs[DEVIEW 2016] 네이버의 모던 웹 라이브러리 - egjs
[DEVIEW 2016] 네이버의 모던 웹 라이브러리 - egjsJae Sung Park
 
Scala the-good-parts
Scala the-good-partsScala the-good-parts
Scala the-good-partsFuqiang Wang
 
Pure Function and Rx
Pure Function and RxPure Function and Rx
Pure Function and RxHyungho Ko
 
JVMLS 2016. Coroutines in Kotlin
JVMLS 2016. Coroutines in KotlinJVMLS 2016. Coroutines in Kotlin
JVMLS 2016. Coroutines in KotlinAndrey Breslav
 
Node-express 채팅 서버 개발기
Node-express 채팅 서버 개발기Node-express 채팅 서버 개발기
Node-express 채팅 서버 개발기정웅 박
 

Viewers also liked (8)

Презентация -ФГОС
Презентация -ФГОСПрезентация -ФГОС
Презентация -ФГОС
 
하스켈학교 세미나 - Haxl
하스켈학교 세미나 - Haxl하스켈학교 세미나 - Haxl
하스켈학교 세미나 - Haxl
 
Continuations in scala (incomplete version)
Continuations in scala (incomplete version)Continuations in scala (incomplete version)
Continuations in scala (incomplete version)
 
[DEVIEW 2016] 네이버의 모던 웹 라이브러리 - egjs
[DEVIEW 2016] 네이버의 모던 웹 라이브러리 - egjs[DEVIEW 2016] 네이버의 모던 웹 라이브러리 - egjs
[DEVIEW 2016] 네이버의 모던 웹 라이브러리 - egjs
 
Scala the-good-parts
Scala the-good-partsScala the-good-parts
Scala the-good-parts
 
Pure Function and Rx
Pure Function and RxPure Function and Rx
Pure Function and Rx
 
JVMLS 2016. Coroutines in Kotlin
JVMLS 2016. Coroutines in KotlinJVMLS 2016. Coroutines in Kotlin
JVMLS 2016. Coroutines in Kotlin
 
Node-express 채팅 서버 개발기
Node-express 채팅 서버 개발기Node-express 채팅 서버 개발기
Node-express 채팅 서버 개발기
 

Similar to Seed2016 - 개미수열 한주영 (annotated)

스칼라와 스파크 영혼의 듀오
스칼라와 스파크 영혼의 듀오스칼라와 스파크 영혼의 듀오
스칼라와 스파크 영혼의 듀오Taeoh Kim
 
Haskell study 10
Haskell study 10Haskell study 10
Haskell study 10Nam Hyeonuk
 
Rnn개념정리
Rnn개념정리Rnn개념정리
Rnn개념정리종현 최
 
Computational Complexity
Computational ComplexityComputational Complexity
Computational Complexityskku_npc
 
R 기초 : R Basics
R 기초 : R BasicsR 기초 : R Basics
R 기초 : R BasicsYoonwhan Lee
 
Linq to object using c#
Linq to object using c#Linq to object using c#
Linq to object using c#병걸 윤
 
Project#2말의여행 Hwp
Project#2말의여행 HwpProject#2말의여행 Hwp
Project#2말의여행 HwpKimjeongmoo
 
Chapter 10 sequence modeling recurrent and recursive nets
Chapter 10 sequence modeling recurrent and recursive netsChapter 10 sequence modeling recurrent and recursive nets
Chapter 10 sequence modeling recurrent and recursive netsKyeongUkJang
 
Mlp logical input pattern classfication report doc
Mlp logical input pattern classfication report docMlp logical input pattern classfication report doc
Mlp logical input pattern classfication report doc우진 신
 
DP 알고리즘에 대해 알아보자.pdf
DP 알고리즘에 대해 알아보자.pdfDP 알고리즘에 대해 알아보자.pdf
DP 알고리즘에 대해 알아보자.pdfHo Jeong Im
 
[Swift] Functions
[Swift] Functions[Swift] Functions
[Swift] FunctionsBill Kim
 
사칙연산 프로그램
사칙연산 프로그램사칙연산 프로그램
사칙연산 프로그램중선 곽
 
Python3 brief summary
Python3 brief summaryPython3 brief summary
Python3 brief summaryHoChul Shin
 
알고리즘과 자료구조
알고리즘과 자료구조알고리즘과 자료구조
알고리즘과 자료구조영기 김
 
Lua 문법 -함수
Lua 문법 -함수Lua 문법 -함수
Lua 문법 -함수Jaehoon Lee
 
파이썬 기본 문법
파이썬 기본 문법파이썬 기본 문법
파이썬 기본 문법SeongHyun Ahn
 

Similar to Seed2016 - 개미수열 한주영 (annotated) (20)

스칼라와 스파크 영혼의 듀오
스칼라와 스파크 영혼의 듀오스칼라와 스파크 영혼의 듀오
스칼라와 스파크 영혼의 듀오
 
Haskell study 10
Haskell study 10Haskell study 10
Haskell study 10
 
Rnn개념정리
Rnn개념정리Rnn개념정리
Rnn개념정리
 
Computational Complexity
Computational ComplexityComputational Complexity
Computational Complexity
 
R 기초 : R Basics
R 기초 : R BasicsR 기초 : R Basics
R 기초 : R Basics
 
함수적 사고 2장
함수적 사고 2장함수적 사고 2장
함수적 사고 2장
 
Linq to object using c#
Linq to object using c#Linq to object using c#
Linq to object using c#
 
Project#2말의여행 Hwp
Project#2말의여행 HwpProject#2말의여행 Hwp
Project#2말의여행 Hwp
 
Chapter 10 sequence modeling recurrent and recursive nets
Chapter 10 sequence modeling recurrent and recursive netsChapter 10 sequence modeling recurrent and recursive nets
Chapter 10 sequence modeling recurrent and recursive nets
 
Mlp logical input pattern classfication report doc
Mlp logical input pattern classfication report docMlp logical input pattern classfication report doc
Mlp logical input pattern classfication report doc
 
Go
GoGo
Go
 
DP 알고리즘에 대해 알아보자.pdf
DP 알고리즘에 대해 알아보자.pdfDP 알고리즘에 대해 알아보자.pdf
DP 알고리즘에 대해 알아보자.pdf
 
R 시작해보기
R 시작해보기R 시작해보기
R 시작해보기
 
ES6-02
ES6-02ES6-02
ES6-02
 
[Swift] Functions
[Swift] Functions[Swift] Functions
[Swift] Functions
 
사칙연산 프로그램
사칙연산 프로그램사칙연산 프로그램
사칙연산 프로그램
 
Python3 brief summary
Python3 brief summaryPython3 brief summary
Python3 brief summary
 
알고리즘과 자료구조
알고리즘과 자료구조알고리즘과 자료구조
알고리즘과 자료구조
 
Lua 문법 -함수
Lua 문법 -함수Lua 문법 -함수
Lua 문법 -함수
 
파이썬 기본 문법
파이썬 기본 문법파이썬 기본 문법
파이썬 기본 문법
 

Seed2016 - 개미수열 한주영 (annotated)

  • 2. 개미수열 1 11 21 1211 111221 312211 13112221 1113213211 ?? n번째 줄 출력하기 개미수열은 앞 줄을 읽어 다음 줄 을 만들어내는 수열 두 번째 줄 “11”을 “2개의 1(영어 식 표현)”이라고 읽어서 “21”이 됨 ?? 로 표시한 9번째 줄은 8번째 줄을 읽어서 구할 수 있음 “3개의 1, 1개의 3, 1개의 2, …”  “31131211131221” 문제: 개미수열의 n(0 기준)번째 줄을 출력하는 함수를 작성하라
  • 3. 열 명 중 아홉 명은… String ant(int n) { String s = "1"; for (int i = 0; i < n; i++) { char c = s.charAt(0); int count = 1; String result = ""; for (int i = 1; i < s.length(); i++) { if (c == s.charAt(i)) count++; else { result += count; result += c; c = s.charAt(i); count = 1; } } result += count; result += c; s = result; } return s; } 대부분 중첩 for로 작성 s는 “1”로 시작하고, n만큼 반복 적용 s를 읽으면서 이미 읽은 글자 c와 비교하고 같으면 count++ 다르면 다음 줄 결과 result에 count와 c를 append
  • 4. 아주 일부는 String ant(int n) { String s = "1"; for (int i = 0; i < n; i++) { s = next(s); } return s; } String next(String s) { char c = s.charAt(0); int count = 1; String result = ""; for (int i = 1; i < s.length(); i++) { if (c == s.charAt(i)) count++; else { result += count; result += c; c = s.charAt(i); count = 1; } } result += count; result += c; return result; } next() 함수를 만들고 반복 적용하 는 식으로 작성하는 경우도 있음. 한 덩어리로 작성하는 것보다는 낫다.
  • 5. 오늘 우리는 • 다양한 방법으로 개미수열을 풀어봅니다
  • 6. JavaScript/Regex • 규칙을 Regular Expression으로 표현할 수 있다면 매우 다행 – 실제로 그렇지 못한 경우가 많음 – 게다가, 정확한 Regex를 작성하는 것은 매우 어려운 일 function next(s) { return s.replace(/(.)1*/g, g => g.length + g[0]) } “1112…” 3 1 g length g[0]
  • 7. Java/Regex • Java에도 JavaScript의 replace같은 함수가 있다면… – 직접 만들면 됨 String next(String s) { return replaceAll(s, "(.)1*", g -> format("%d%c", g.length(), g.charAt(0))); } String replaceAll(String s, String regex, UnaryOperator<String> f) { StringBuffer sb = new StringBuffer(); Matcher m = Pattern.compile(regex).matcher(s); while (m.find()) { String g = m.group(); m.appendReplacement(sb, f.apply(g)); } m.appendTail(sb); return sb.toString(); } 기본 틀은 compile/matcher/find/group 추가로, appendReplacement와 appendTail을 이용하면 JavaScript의 replace처럼 동작하는 도움함수를 작성 할 수 있음 Java8의 UnaryOperator를 이용
  • 8. App-Specific vs. General • 처음 ant(n)는 한 덩어리 • next(s)만 분리되어도 좀 나음 • next(s)도 Regex로 한단계 더 분리되었음 – replaceAll(s,regex,f)은 일반적인 함수 ant(n) next(s) replaceAll(s, regex, f) 문제를 잘게 나누다보면 결국 일반적인 작은 문 제들이 되고, 이런 작은 문제들은 빈번하게 등장하게 된다. replaceAll(s,regex,f)는 어디나 써먹을 수 있음
  • 9. next() 함수를 더 나누기 • 열명 중 아홉이 짜는 next() String next(String s) { char c = s.charAt(0); int count = 1; String result = ""; for (int i = 1; i < s.length(); i++) { if (c == s.charAt(i)) count++; else { result += count; result += c; c = s.charAt(i); count = 1; } } result += count; result += c; return result; } count compare loop format/append
  • 10. next는 리스트 프로세싱 • List<Integer> next(List<Integer> ns) – 리스트를 통째로 다루기 13112221 1 3 11 222 1 11 13 21 32 11 1113213211 32 List<List<A>> group(List<A> as) List<B> map(Function<A,B> f, List<A> as) List<A> concat(List<List<A>> ass) 다른 접근을 살펴보기 위해 String 대 신 List<Integer>로 바꿔보자! 리스트를 처리하는 group/map/concat 도움함수는 signature만 보더라도 매우 일반화된 함수라는 것을 알 수 있음
  • 11. next()는 리스트 프로세싱 List<Integer> next(List<Integer> ns) { return concat(map(g => listOf(g.size(), g.get(0)), group(ns)); } ant(n) next(s) group (list) map(f, list) concat(list-of-list) String에서 replaceAll같은 일반화된 도움함수를 사용 한 것처럼 List에 대해 일반화된 도움함수를 얻을 수 있 고, 이를 이용하면 next()는 아주 간단히 해결됨 for/==/++/+= 등의 primitive는 감춰지기 때문에 실수할 여지가 줄어듬
  • 12. Recap • ant/next – for/count/==/+=/… • next/regex – replace(regex, f) • next/list-processing – concat/map/group
  • 13. 개미수열 n == 100 1 11 21 1211 111221 312211 13112221 1113213211 .. .. .. ... ?? 100 번째 줄 출력하기
  • 14. 개미수열 OutOfMemoryError • 한 줄마다 길이가 30%씩 증가 – 100번째 줄의 길이는?? – 대충… 5천억 = 5e11 = 500G • 그럼 어떻게? https://en.wikipedia.org/wiki/Look- and-say_ sequence 이미 이 수열의 각 줄이 30%씩 증가 한다는 것을 증명한 수학자가 있음 100번째 줄은 String/List 에 담을 수 도 없다. 출력하기 위해 꼭 String/List에 담아 둘 필요는 없음
  • 15. Iterator • 무한 수열을 나타낼 수 있음 – boolean hasNext() { return true; } Iterator<Integer> ant(int n) { Iterator<Integer> s = asList(1).iterator(); for (int i=0; i<n; i++) s = new Next(s); return s; } class Next implements Iterator<Integer> { ... }
  • 16. Iterator • 100번째 줄 위로는 필요한 만 큼만 계산 – lazy evaluation • 메모리 문제는 없지만 5천억개가 출력될 때까지 지켜봐야 함  1 Next Next Next Next Next while (s.hasNext()) System.out.print(s.next()); Next는 또다른 Iterator를 포함하 는 Wrapper Iterator
  • 17. 개미수열 n == 100 1113122113121113222123211211131211121311 1213211231132132211211131221232112111312 2112131112131221121321132132211231131122 2113311213212322211211131221131211221321 1231132132211211131221131211132221121311 1213122112132113121113222112132113213221 1331121321232221123113112221131112311322 3112111311222112132113311213211221121332 2112111312211312111322212311222122132113 2132211231131122211331121321232221121113 1221131211132221232112111312111213322112 1311121312211213211312111322211213211321 32212321121113121112…
  • 18. class Next implements Iterator<Integer> { public Integer next() { if (state == State.INIT) { state = State.HAS_NEXT; next = inner.next(); } if (state == State.HAS_NEXT) { state = State.LAST; elem = next; count = 1; while (inner.hasNext()) { int next = inner.next(); if (next == elem) { count++; } else { state = State.COUNT; this.next = next; break; } } return count; } else if (state == State.LAST) { state = State.INIT; return elem; } else { state = State.HAS_NEXT; return elem; } } Next 검토 • 그런데, 다시 처음 그 문제 가… – 한 덩어리 Next.next() • 그건 그렇고, 왜 for문보다 더 복잡하지? – loop를 while(hasNext())로 넘겼음 – 상태변수를 따로 두어야 함 – 연속해서 값을 생성하기 어 려움
  • 19. 리스트 프로세싱 • 한 덩어리 문제는 해결됨 • 복잡해진 진짜 원인은 loop를 빼앗긴 탓 – Iterator는 Loop 컨트롤을 외부로 빼앗겼음 – 상태 유지가 더 힘들어졌음 Iterator<Integer> next(Iterator<Integer> s) { return new Concat(new Map(g -> …, (new Group(s))); } Concat/Map/Group은 Decorator Map/Group을 합쳐서 RunLength 이터레이터를 만들 수도 있음 Concat/Map을 합쳐서 ConcatMap 이터레이터를 만들어도 됨
  • 20. JavaScript/Generator • 상태를 가지는 Iterator를 쉽게 만들 수 있는 도구 function *next(line) { let prev = line.next().value let count = 1 for (let c of line) { if (prev === c) count++ else { yield count; yield prev prev = c count = 1 } } yield count; yield prev } for를 가지고 있다! loop 내에서 여 러 값을 출력할 수도
  • 21. Java/Generator • Generator는 – yield에서 control을 놓고 – next()호출하면 resume • 일종의 Coroutine – Java에서는 Thread로 Generator Coroutine을 구현할 수 있 음 Generator<Integer> ints = Generator.of((g) -> { int i = 0; while (true) g.yield(i++); }); for (int i = 0; i < 100; i++) { System.out.println(ints.next()); } Thread로 구현하면 Thread를 종료시켜 줘야 하는 문제가… 일단 Generator interface는 쉽게 구현 할 수 있음. JavaScript의 Generator처럼 사용 가능
  • 22. • 코루틴은 서브루틴보다 더 일반적인 개념. • 서브루틴은 call/return 뿐이지만, 코루틴은 suspend/resume이 가능. • 코루틴이 suspend하지 않으면 그것이 서브루틴 • 이런의미로 앞의 Generator는 일종의 코루틴 (코루틴은 다른 코루틴을 suspend하면서 다른 코루틴을 resume할 수있는데, Generator는 suspend하면 호출한 쪽으로 되돌아감) • Iterator/infinite list를 만드는데 사용할 수 있다고 나와 있음
  • 23. Go/goroutine • Go 언어는 고루틴/채널을 기본 제공 • go f() – f()함수를 새로운 고루틴에서 실행 • c := make(chan int) – 고루틴 간의 통신은 채널을 이용 – c  0 : c로 값을 전달 –  c : c에서 값을 읽음
  • 24. Go/goroutine 1 Next Next Next Next Next 고루틴 채널 ch := make(chan int) go func() { ch <- 1 close(ch) }() for i := 0; i < n; i++ { ch1 := make(chan int) go next(ch, ch1) ch = ch1 } func next(in, out chan int) { ... generator와 거의 같음 } 채널 연산은 yield/resume 역할을 함 Go에서 고루틴을 이용하여 작성하면 n == 100을 쉽 게 출력할 수 있음
  • 25. Java/goroutine • Thread/BlockingQueue를 이용하여 go/send/recv/close 만들기 interface Goroutine { void run() } static void go(Goroutine go) { new Thread(() -> go.run()).start(); } class Chan<A> { SynchronousQueue<Option<A>> queue = new … void send(A a) { queue.put(option(a)); } Option<A> recv() { return queue.take(); } void close() { queue.put(option()); } } Chan<Integer> ch = new . go(() -> { ch.send(1); ch.close(); })
  • 26. Recap • Iterator – next() • Generator – next()/yield • Coroutine – with Thread/goroutine n == 100에서 어떻게  Iterator로 변형 Iterator 구현이 지저분함  JavaScript의 Generator  Java로 …  Go의 Goroutine  Java로 …
  • 27. 개미수열 n == 10000 1 11 21 1211 111221 312211 13112221 1113213211 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ?? 10000 번째 줄 출력하기
  • 28. 개미수열 StackOverflowError • Iterator.next -> next -> next … – Generator는 producer 코루틴을 위한 것 – Next는 transducer 코루틴(출력 뿐 아니라 입력도 필요하다) • Thread를 이용한 Coroutine의 경우에는 OutOfMemoryError – OutOfMemoryError: unable to create new native thread • Go는 괜찮은데.. – Goroutine은 Green thread • 그럼 어떻게? – Go의 Green thread를 흉내내거나 – Stack을 사용하지 않는 Coroutine을 만들거나
  • 29. 개미수열 복잡도 50번째 줄을 출력하는 과정 살펴보기 맨 아랫줄에 빨간색 칸은 그 위치의 값을 계산하려 고 suspen되었음을 보여주고 그 앞줄/그 앞줄… 도 suspend 그러다 값이 계산되면 차례로 resume/resume/resume될 것 어느 한 순간에는 하나의 Coroutine만 Active!
  • 30. Coroutine의 본질 • Cooperative multitasking (non-preemptive) – yield/resume – 각각을 쓰레드로 보더라도 동시에 실행되지는 않음 • producer  transducer  consumer start yield 데이터 요청 resume yield 데이터 요청 resume yield 데이터 생성 resume yield 데이터 요청 resume yield 데이터 생성 yield 데이터 생성 resume startstart Go에서는 채널에 데이터 요청/전달 나머진 거의 같음 • 첫줄(1을 출력)은 Producer, • 앞줄을 읽어 다음 줄을 생성하는 Next()는 Transducer, • n번째 줄을 출력하는 건 Consumer임 코루틴이 yield하는 이유가 두가지 (요청/생성)
  • 31. resume/yield • 우리가 가진 건 서브루틴 뿐 – call/return • return할 내용 – yield하는 이유(값 요청? 전달?) – 다시 resume할 위치 • call할 때 전달할 내용 – resume할 위치 – 요청 값 • 코루틴들을 organize할 dispatcher 함수 필요 – 각 코루틴들의 상태를 기억(스택 변수는 쓸모없음) • 심지어 C로도 가능하다
  • 32. C/Coroutine typedef struct state { char prev; // 이전에 읽은 값 char count; // 현재까지 누적 카운트 char next; // 다음으로 읽은 값 char ptr; // resume할 위치 } state; int init(state *s) { switch (s->ptr) { case 0: s->ptr = 1; return 1; default: return 0; } } 반환값 약속 • -1: 값 요청 • 0: 스트림 종료 • 1/2/3: 값 전달 func init(i, o) { o <- 1 close(o) } go init(ch) function *init() { yield 1 return } resume 위치를 반환하 는 방법도 있음
  • 33. C/Coroutine typedef struct state { char prev; // 이전에 읽은 값 char count; // 현재까지 누적 카운트 char next; // 다음으로 읽은 값 char ptr; // resume할 위치 } state; int init(state *s) { switch (s->ptr) { case 0: s->ptr = 1; return 1; default: return 0; } } int next(state *s) { switch (s->ptr) { case 0: s->ptr = 1; return -1; case 1: s->prev = s->next; s->count = 1; s->ptr = 2; return -1; case 2: if (s->prev == s->next) { s->count++; return -1; } else if (s->next == 0) { s->ptr = 3; return s->count; 반환값 약속 • -1: 값 요청 • 0: 스트림 종료 • 1/2/3: 값 전달 값을 읽기 위해 yield ptr 조작 X loop!! resume resume 다음 값을 읽음 yield
  • 34. C/Coroutine int n = 1000000; state* lines = (state*)calloc(n + 1, sizeof(state)); int cur = n + 1; while (1) { int result = (cur == 0) ? init(&lines[0]) : next(&lines[cur]); switch (result) { case -1: // read cur--; break; default: // close or write 1/2/3 if (cur < n) { cur++; lines[cur].next = result; } else { printf("%d", result); } } 값을 읽으려면 선행 코루틴 실행해야 다음 코루틴으로 값을 전달하고 resume
  • 35. 개미수열 복잡도 • 공간 복잡도 – O(n) • 시간 복잡도 – n번째 줄 m번째 글자까지 출력 – O(n + m log m)
  • 36. C/Coroutine 검토 • No abstraction – 중복 코드를 제거할 수도 없다 – 함수를 분리할 수도 없다
  • 37. Coroutine vs. Continuation • Coroutine – Thread 이용 • 실제로 pause/resume – resume pointer • Continuation을 반환하는 것으로 이해할 수 있음 • Continuation – ptr 반환후 resume할 때 jump 하는 대신 – 다음 실행할 continuation을 closure로 반환 – resume은 continuation을 호출하는 것
  • 38. JavaScript/CPS • Continuation Passing Style – setTimeout(continuation, 1000) • 1초뒤 실행할 내용을 continuation에 담아 전달 function init() { return write(1, undefined) } function write(value, cont) { return { type: 'write', cont } } 1을 전달하고 다음 실행할 내용은 없다
  • 39. JavaScript/CPS function init() { return write(1, undefined) } function next() { return read(c => loop(c, 1)) function loop(prev, count) { return read(c => { if (typeof c === 'undefined') return write(count, () => write(prev, undefined)) else if (prev === c) return loop(prev, count + 1) else return write(count, () => write(prev, () => loop(c, 1))) }) } } 첫 글자 읽고 loop 진입 loop에서 글자 읽어서 종료? count/prev 출력 후 종료 같음? count증가 후 loop 반복 다름? count/prev 출력 후 새로 읽은 글자로 반복
  • 40. JS/CPS 검토 • write2 같은 추상화 가능 • Callback Hell – 흐름을 추적하기 어려움 – Promise같은 CPS 추상화 필요 function write2(a, b, cont) { return write(a, () => write(b, cont)) }
  • 41. JS/Promise • Callback에 대한 추상화 – 직접 Callback 인자를 받고, Callback을 호출하는 대신 – Callback을 처리할 Promise 객체를 반환 – Promise가 Callback을 처리 – Chaining이 가능한 then 메쏘드 step1(arg1, (res1) => { step2(arg2, (res2) => { step3(arg3, (res3) => { ... }) }) }) step1(arg1) .then((res1) => ... step2(arg2)) .then((res2) => ... step3(arg3)) .then((res3) => ...)
  • 42. Read/Write 추상화 class Read { constructor(cont) { this.cont = cont } then(f) { return new Read(x => this.cont(x).then(f) } } class Write { constructor(value, cont) { this.value = value; this.cont = cont } then(f) { return new Write(this.value, this.cont.then(f)) } } function read() { return new Read(undefined) } function write(value) { return new Write(value, undefined) }
  • 43. Read/Write 추상화 function init() { return write(1) } function next() { return read() .then(c => loop(c, 1)) function loop(prev, count) { return read() .then(c => { if (typeof c === 'undefined') return write2(count,prev) else if (prev === c) return loop(prev, count + 1) else return write2(count, prev) .then(() => loop(c, 1)) }) } }
  • 44. CPS 추상화 검토 • 추상화를 깔고 새로운 추상화 • OOP Design Patterns의 Interpreter패턴 – Read/Write 는 cont를 가지며 Composite function forever(program) { return program.then(() => program) } const echo = read().then(write) const prog = forever(echo) run(prog)
  • 45. Recap • hand-written Coroutine • JS/Continuation passing style • CPS 추상화
  • 46. 무한 수열 • 더 일반적인 방법 – Stream/Lazy list
  • 47. Scala/Haskell • Scala는 Stream[A] 라는 Lazy list를 지원 • Haskell은 기본 리스트가 Lazy (사실 전부 lazy) def ant = Stream.iterate(Stream(1))(next) def next(s: Stream[Int]) = group(s) flatMap {g => Stream(g.size, g.head)} def group[A](as: Stream[A]): Stream[Seq[A]] = ... ant(1000000)(1000000) // 1M번째 줄 1M번째 글자 ant = iterate(group >=> sequence[length, head]) [1]
  • 48. Java/JS • Java와 JS는 동적 언어 • 쉽게 Lazy list를 만들 수 있다. • Lazy list의 핵심은 Linked list의 Tail을 필요할 때 생성하기 class List<A> { ... public List(A head, Supplier<List<A>> tail) { ...} public A head() { return head; } public List<A> tail() { return tail.get(); } } List<Integer> intsFrom(int n) { return new Node(n, () => intFrom(n+1)) }
  • 49. Java/JS • Java와 JS는 동적 언어 • 쉽게 Lazy list를 만들 수 있다. • Lazy list의 핵심은 Linked list의 Tail을 필요할 때 생성하기 class List<A> { ... public List(A head, Supplier<List<A>> tail) { ...} public A head() { return head; } public List<A> tail() { return tail.get(); } } List<Integer> intsFrom(int n) { return new Node(n, () => intFrom(n+1)) }
  • 50. List vs. Stream • List/Stream은 같은 추상화의 동작만 다 른 형태 List<Integer> next(List<Integer> ns) { return concat(map(g => listOf(g.size(), g.get(0)), group(ns)); } Stream<Integer> next(Stream<Integer> ns) { return concat(map(g => streamOf(g.size(), g.head()), group(ns)); }
  • 51. Recap • for-loop, regex • list processing – stream(lazy list)도 마찬가지 • iterator/generator/coroutine – Thread로 흉내내기 – Coroutine의 본질 – resume ptr • Continuation passing – CPS 추상화 : Interperter 패턴