什么是简单的一次性技术?

时间:2014-02-17 16:52:48

标签: language-agnostic

无论执行多少次函数,只需执行一次操作的简单技巧是什么?是否有任何编程语言都有内置的特定方法来处理这个常见的问题?

示例:initialize()只应在第一次执行时将global_variable设置为true。

一个c ++示例(出于好奇而寻找替代方案 - 不是必需的):

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;
    }
}

3 个答案:

答案 0 :(得分:2)

除了可用于任何语言的全局变量技术外,还有其他几种方法可以做到这一点。

在使用静态变量而不是全局的静态变量的语言中,最好是为了防止全局范围内的变量名称冲突。

在某些语言中,您可以在运行时重新定义/重新声明函数,以便您可以执行以下操作:

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

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

在某些语言中,由于范围问题(内部函数),您无法在函数内重新声明函数,但仍可以将其他函数重新分配给现有函数。所以你可以这样做:

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

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

某些语言(尤其是声明性语言)实际上内置了一个“latch”功能,只执行一次。有时甚至还有重置功能。所以你实际上可以这样做:

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

如果语言允许,您可以重置do_once指令,如果您真的想重新执行该功能:

reset initialize;
initialize();

注意:上面的类C语法显然是伪代码,并不代表任何真实语言,但所描述的功能确实存在于真实语言中。此外,程序员很少遇到除HTML,XML和CSS之外的声明性语言,但图灵完整的声明性语言确实存在并且通常用于硬件设计,而“do_once”功能通常编译为D触发器或锁存器。

答案 1 :(得分:1)

Eiffel有一次例程的内置概念。一次例程仅在第一次调用时执行,在下一次调用时不执行。如果例程是函数,即返回结果,则为所有后续调用返回第一次执行的结果。如果第一个调用以异常终止,则会为所有后续调用引发相同的异常。

一次性函数foo的声明看起来像

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

在多线程环境中,可能需要区分不同线程使用的对象。这是通过向声明添加键"THREAD"来实现的(它实际上是默认值):

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

如果所有线程必须共享同一个对象,则使用密钥"PROCESS"

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

相同的语法虽然没有返回类型,但用于过程。

一旦整个过程保证只执行一次例程。因为竞争条件是可能的,所以Eiffel运行时确保一次最多只有一个线程可以触发对给定的一次例程的评估。其他线程将暂停,直到主要执行完成,以便他们可以使用单个结果或确保仅执行一次操作。

在其他方面,一旦例程与常规例程没有区别,在某种意义上它们遵循面向对象编程的相同规则,如继承和重新声明(覆盖)。因为这是一个正常的例程,它可以直接或间接地调用其他例程。当发生这样的递归调用时,不会再次执行once例程,而是返回结果的最后已知值。

答案 2 :(得分:0)

是的,有些语言(scala)支持它(使用懒惰)但通常这个功能是由框架提供的,因为有一些权衡。有时您需要线程级别,阻止同步。有时分拆就足够了。有时您不需要同步,因为简单的单线程缓存就足够了。有时您需要记住许多计算值,并且您愿意忘记最近使用过的那些值。等等。可能这就是为什么语言通常不支持这种模式 - 这就是框架的工作