반복문 속 값을 한번만 평가되도록 만들기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>

#define eval_once(exp) ([&](){static auto memo = exp;return memo;})()

size_t len()
{
    puts("size_t len()");
    return 4;
}

int main()
{
    for(size_t i=0;i!=len();++i)
        std::cout << i << "\n";
    std::cout << "--------\n";
    for(size_t i=0;i!=eval_once(len());++i)
        std::cout << i << "\n";
}

#if 0
size_t len()
0
size_t len()
1
size_t len()
2
size_t len()
3
size_t len()
--------
size_t len()
0
1
2
3
#endif

동기

1
2
for(int i=0;i<strlen(str);i++) // O(n^2)
    cout << str[i];

물론 보통은 이렇게 만든다.

1
2
for(int i=0;str[i];++i) // O(n)
    cout << str[i];

왜 저런 동작을 하는가?

람다 표현식은 다른 곳에 함수를 만들고 그 참조를 가져오는 것이므로, 매번 새로 만들어 지지 않는다. 따라서 일반 함수의 static 변수 처럼 작용한다.

static 변수의 초기화

static 변수의 초기화는 반드시 한번만 일어난다. 즉, 한번만 쓸 수 있는 함수를 만들 수 있다.

1
2
3
4
5
6
7
8
9
int f(int k)
{
    static int a = k;
    return a;
}

...
f(0) // 0
f(1) // 0

한계점

define을 이용하므로, 비 직관적이다. 평가 된 후 eval_once를 호출하는것으로 보여 효과가 있을지 처음 보는 사람에겐 혼란을 준다. 이게 Well-defined인지는 내일의 나에게 물어보도록 하자