如何印清單

2005 年我問過 Thinker 這樣的問題:想程式常碰到要印出清單的情況,對人類而言習慣的格式是:

1
1, 2, 3, 4

但這個格式對於 C 語言來說卻不太好處理。一般可以寫成這樣:

1
2
3
4
5
6
7
8
9
10
11
<<一般作法>>=
void normal(const char *list[], const int len)
{
int i = 0;
printf("%s", list[i++]);
for (; i < len; i++) {
printf(", %s", list[i]);
}
puts("");
}
@

但是 printf 這行會重複,似乎不是最好的寫法。如果不要重複,那就得在迴圈中加個判斷式,但每次都要多個判斷又好像有點浪費:

1
2
3
4
5
6
7
8
9
10
11
12
<<判斷作法>>=
void condition(const char *list[], const int len)
{
int i;
for (i = 0; i < len; i++) {
if (i != 0)
printf(", ");
printf("%s", list[i]);
}
puts("");
}
@

當時 Thinker 想了想,給了我一個用 function pointer 的答案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void dummy() {
}

void line() {
printf(" ---


---


\n");
}

inter = &dummy;
for(i = 0; i < n; i++) {
inter();
printf("%s\n", record[i]);
inter = &line;
}

話說,事隔多年,今天在看其他東西的時候,突然想到這個問題可以用 Clifford’s Device 的方法做。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<<Clifford>>=
void clifford(const char *list[], const int len)
{
int i = 0;
while (1) {
if (0) {
clifford:
printf(", ");
}
printf("%s", list[i++]);
if (i >= len)
break;
goto clifford;
}
puts("");
}
@

各位看官,可有什麼新想法嗎?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<<list.c>>=
#include <stdio.h>

<<一般作法>>

<<判斷作法>>

<<Clifford>>

int main(int argc, char *argv[])
{
const char *list[] = {
"one",
"two",
"three",
};
const int l = sizeof(list) / sizeof(char *);

normal(list, l);
condition(list, l);
clifford(list, l);
return 0;
}
@

當然啦,如果是 python,這問題可簡單了:

1
2
3
>>> l = ['one', 'two', 'three']
>>> print ', '.join(l)
one, two, three

另,本文採用 Noweb 格式,用工具跑一遍就可以產生 list.c 。