如何印清單

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

1, 2, 3, 4

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

<<一般作法>>=
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 這行會重複,似乎不是最好的寫法。如果不要重複,那就得在迴圈中加個判斷式,但每次都要多個判斷又好像有點浪費:

<<判斷作法>>=
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 的答案

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 的方法做。

<<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("");
}
@

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

<<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,這問題可簡單了:

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

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

Advertisements

bug in python-opengl + mesa

Since like forever, I have never succeed in executing python-opengl demos on debian lenny.  It always segfault during glutInitDisplayMode.  After some debugging, here is why.

One should be able to call glGetError at any time.  If there’s nothing wrong, it should just return something like GLNOERROR.  Any raw (native) function call in python-opengl, no matter it belongs to GL, GLU or GLUT, will always call glGetError right after each call to check for error.  I think this is wrong since functions like glutInitDisplayMode have NOTHING to do with glGetError.  With mesa 7.0.3-5 in my system, the call to glGetError without glInit will cause segfault.  I check the same thing on Ubuntu, which has an older version of mesa and python-opengl, and it does not happen.

python-opengl enabled error checking by default with OpenGL.ERROR_CHECKING, which is set to True in OpenGL/__init__.py.  This code snippet can disable it:

import OpenGL
OpenGL.ERROR_CHECKING = False
# import other stuffs such as OpenGL.GL, etc.

or you can just modify the __init__.py to disable it by default.

Given the current status of python-opengl and low level x protocol support in python, I think the best language to do 3D in FOSS world will still be pure simple C.

我果然不懂 C 啊。

from gcc-4.2.info:

5.34 An Inline Function is As Fast As a Macro

<..snipped..>

If you specify both `inline’ and `extern’ in the function definition,
then the definition is used only for inlining. In no case is the
function compiled on its own, not even if you refer to its address
explicitly. Such an address becomes an external reference, as if you
had only declared the function, and had not defined it.

This combination of `inline’ and `extern’ has almost the effect of a
macro. The way to use it is to put a function definition in a header
file with these keywords, and put another copy of the definition
(lacking `inline’ and `extern’) in a library file. The definition in
the header file will cause most calls to the function to be inlined.
If any uses of the function remain, they will refer to the single copy
in the library.

Since GCC 4.3 will implement ISO C99 semantics for inline functions,
it is simplest to use `static inline’ only to guarantee compatibility.
(The existing semantics will remain available when `-std=gnu89′ is
specified, but eventually the default will be `-std=gnu99′; that will
implement the C99 semantics, though it does not do so in versions of
GCC before 4.3. After the default changes, the existing semantics will
still be available via the `-fgnu89-inline’ option or the `gnu_inline’
function attribute.)

GCC does not inline any functions when not optimizing unless you
specify the `always_inline’ attribute for the function, like this:

/* Prototype. */
inline void foo (const char) __attribute__((always_inline));

So, consider the following program:

gcctest.c:

#include "inline.h"

int main()
{
     puts(externinline());
     return 0;
}

inline.h:

#include 

__inline__ extern char *externinline()
{
     return "inline";
}

lib.c:

char *externinline()
{
     return "extern";
}

The behavior of the executables will be different with or without the -O option of gcc. Compile it without -O, the program will print “extern”. Compile it with -O, the program will print “intern”.

GLOBAL and struct

Refer to: Gnu – Global – Help – How to find the definition effectively

To put the story even shorter, the GNU GLOBAL will not treat C struct as definition, but as `other symbol’ . This means you may only find a struct with global -s, which will give you lots of irrelevant results.

This feature is in the GLOBAL plans for the future already, which means it might be implemented sometime in the future. For now, it’s better to use GLOBAL to trace how the program runs, the relationship between functions, rather then finding the semantic details. For the semantic side, the ctags or etags is still my first choice.