# * 서브루틴, 순수한 함수

Written by 📝 Wabi

# 서브루틴

다른 코드에서 procedure, routine, subprogram, macro 등 다양하게 불려지는 서브루틴은 자바스크립트에서는 함수 또는 메서드라고 불러지고 프로그래밍에 있어서 단순반복이 연속되는 부분을 일부 떼어내어 코드를 간단하게 만드는 것을 말한다. (서브루틴 === 알고리즘)


# 예시 1. : 서브루틴 function

const hour = new Date().getHours();
let Lunch = false;
if(12<=hour && hour<=13) Lunch = true;
1
2
3

위 코드가 여러번 반복되어야 해서, 하단에 함수로 바꾸면,

function isCurrentLunch(){
    const hour = new Date().getHours();
    let Lunch = false;
    if(12<=hour && hour<=13) Lunch = true;
    else Lunch = false;
    return Lunch;
}
1
2
3
4
5
6
7

함수명에 반환값이 불리언 이라면 is를 붙이는 것이 일반적이고, 함수명에 current를 넣은 이유, 현재 시간에 따라 항상 값이 다르기 때문이다.


# 순수한 함수 : pure function

위 예시 1 코드는 순수한 함수라고 부를 수 없다. 순수한 함수('pure function')은 입력값이 같을 때, 결과값도 같은 수학적인 정의에 충실한 함수를 말한다.
따라서, 위 예시 1 코드는 시간에 따라 결과값이 달라서 순수한 함수라고 부를 수 없다.

# 예시 1 코드를 순수한 함수로 바꿀시,

function isCurrentLunch(hour){
    let Lunch = false;
    if(12<=hour && hour<=13) Lunch = true;
    else Lunch = false;
    return Lunch;
}
1
2
3
4
5
6

이제는 입력값이 같으면 결과도 항상 같고, 다른 부수적인 효과를 일으키지도 않기 때문에 순수한 함수라고 볼 수 있다.

# 예시 2. : pure function

'다른 부수적인 효과' 가 일어나는 function 예시 (순수한 함수가 아닌)

const people = ['wabi','yuna','hidekuma','one','teak','twice'];
let idx = -1;
function getNextPerson(){
    if(++idx >= people.length) idx = 0;
    return people[idx];
}
1
2
3
4
5
6
  • 함수의 결과가 항상 다르다.
  • idxgetNextPerson 함수에 속하지 않는데도 함수를 호출하면 변수가 바뀐다.

위 2가지가 순수한 함수라는 정의를 어긴다.

위 코드 수정 : 클로저 이용해서 함수안에 변수를 넣기

const getNextPerson = (function(){
    const people = ['wabi','yuna','hidekuma','one','teak','twice'];
    let idx = -1;
    return function(){
        if(++idx >= people.length) idx = 0;
        return people[idx];
    };
})();
1
2
3
4
5
6
7
8

하지만 입력이 같아도 결과가 다르기 때문에 순수 함수라고 볼 수 없다.
만약 위 함수 getNextPerson를 다른곳에서도 사용한다고하면, 결과값에 영향을 끼치게 되므로 부수적인 효과가 일어난다.

test1 = getNextPerson;
test2 = getNextPerson;

test1(); // 'wabi'
test1(); // 'yuna'
test1(); // 'hidekuma'
test2(); // 'one'
1
2
3
4
5
6
7

위 코드 수정 : 디자인 패턴(반복자, iterator), 이터레이터를 사용하기

//const people = ['wabi','yuna','hidekuma','one','teak','twice'];
const getNextPerson = (function(){
    function getNextPerson(people){
        this.people = people;
        this.idx = 0;
    };
    getNextPerson.prototype.next = function() {
        return this.people[this.idx++];
    };
    getNextPerson.prototype.done = function() {
        return this.people.length === this.idx;
    };
    return getNextPerson;
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14

위 코드의 getNextPerson함수는 순수한 함수이다.
항상 같은 것을 반환 하기 때문에 안전하다. 결국에는 next() 메서드도 매번 다른 값을 반환하기 때문에 순수한 함수가 아니다? 라고 할 수도 있지만, 반환 자체가 메서드라는 점, 자신이 속한 객체 안에서 동작하기 때문에 getNextPerson 함수를 다른곳에 또 호출하더라도 독립적인 이터레이터가 생성된다. (= 다른 이터레이터를 침범하지 않는다.)

test1 = new getNextPerson(['wabi','yuna','hidekuma','one','teak','twice']);
test2 = new getNextPerson(['wabi','yuna','hidekuma','one','teak','twice']);

test1(); // 'wabi'
test1(); // 'yuna'
test1(); // 'hidekuma'
test2(); // 'wabi'

test3 = new getNextPerson(['wabi','yuna','hidekuma','one','teak','twice']);
while (!test3.done()) {
  console.log(test3.next());
}
// 'wabi'
// 'yuna'
// 'hidekuma'
// 'one'
// 'teak'
// 'twice'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

etc. 제너레이터 (ES6) 사용하기

const people = ['wabi','yuna','hidekuma','one','teak','twice'];
function* getNextPerson(people){
    const length = people.length;
    for(let idx = 0; idx < length; idx ++){
        yield people[idx];
    }
}

test1 = getNextPerson(people);
test2 = getNextPerson(people);

test1.next(); // {value: "wabi", done: false}
test1.next(); // {value: "yuna", done: false}
test1.next(); // {value: "hidekuma", done: false}
test1.next(); // {value: "one", done: false}

test2.next(); // {value: "wabi", done: false}

test1.next(); // {value: "teak", done: false}
test1.next(); // {value: "twice", done: false}
test1.next(); // {value: undefined, done: true}

test2.next(); // {value: "yuna", done: false}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

위 코드 getNextPerson 함수 또한 제너레이터를 이용한 순수한 함수이다.

Last Updated: 7/5/2019, 8:38:55 AM