Domanda

What is a simple technique to perform some action just once, no matter how many times the function is executed? Do any programming languages have specific ways built-in to handle this somewhat common problem?

Example: initialize() shall set global_variable to true on ONLY its first execution.

A c++ example (looking for alternatives to this out of curiosity - not necessity):

init.h:

int global_variable;
void initialize(void);

init.c:

static bool already_initialized = false;
void initialize(void)
{
    if (!already_initialized) 
    {
        already_initialized = true;
        global_variable = true;
    }
}
È stato utile?

Soluzione

Apart from the global variable technique that's available in any language there are several other ways to do this.

In languages that have static variables using a static variable instead of global is preferable in order to prevent variable name collisions in the global scope.

In some languages you can redefine/redeclare functions at runtime so you can do something like this:

function initialize (void) {
    // do some stuff...

    function initialize(void) {}; // redefine function here to do nothing
}

In some languages you can't quite redeclare functions within functions due to scope issues (inner functions) but you can still reassign other functions to an existing function. So you can do something like this:

function initialize (void) {
    // do some stuff ...

    initialize = function (void) {}; // assign no-op anonymous function
                                     // to this function
}

Some languages (especially declarative languages) actually have a "latch" functionality built in that executes just once. Sometimes there is even a reset functionality. So you can actually do something like this:

function do_once initialize (void) {
    // do some stuff
}

If the language allows it you can reset the do_once directive if you really want to re-execute the function:

reset initialize;
initialize();

Note: The C-like syntax above are obviously pseudocode and don't represent any real language but the features described do exist in real languages. Also, programmers rarely encounter declarative languages apart from HTML, XML and CSS but Turing complete declarative languages do exist and are typically used for hardware design and the "do_once" feature typically compiles down to a D flip-flop or latch.

Altri suggerimenti

Eiffel has a built-in notion of once routines. A once routine is executed only the first time it is called, on the next call it is not executed. If the routine is a function, i.e. returns a result, the result of the first execution is returned for all subsequent calls. If the first call terminates with an exception, the same exception is raised for all subsequent calls.

The declaration of the once function foo looks like

foo: RETURN_TYPE
    once
        ... -- Some code to initialize Result.
    end

In a multithreaded environment it might be desirable to distinguish objects used by different threads. This is accomplished by adding the key "THREAD" to the declaration (it is actually the default):

foo: RETURN_TYPE
    once ("THREAD")
        ...
    end

If the same object has to be shared by all the threads, the key "PROCESS" is used instead:

foo: RETURN_TYPE
    once ("PROCESS")
        ...
    end

The same syntax though without return type is used for procedures.

Process-wide once routines are guaranteed to be executed just once for the whole process. Because race conditions are possible, Eiffel run-time makes sure at most one thread may trigger evaluation of the given once routine at a time. Other threads become suspended until the primary execution completes so that they can use the single result or be sure the action is performed only once.

In other respects once routines are no different from the regular routines in a sense that they follow the same rules of object-oriented programming like inheritance and redeclaration (overriding). Because this is a normal routine it can call other routines, directly or indirectly involving itself. When such a recursive call occurs, the once routine is not executed again, but the last known value of the result is returned instead.

yes, some languages (scala) does support it (using lazy) but usually this functionality is provided by frameworks because there are some trade offs. sometimes you need thread level, blocking synchronization. sometimes spin-offs is enough. sometimes you don't need synchronization because simple single-threaded cache is enough. sometimes you need to remember many calculated values and you are willing to forget last recently used ones. and so on. probably that's why languages generally don't support that pattern - that's frameworks' job

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top