vic*_*ory 1 c artificial-intelligence function dynamic
我有一个不寻常的问题.让我们想象一下我有N个功能:
void function1(){ //some code here }
void function2(){ //some code here }
...
void functionN(){ //some code here }
Run Code Online (Sandbox Code Playgroud)
有没有办法,我可以动态地计算或找出哪个函数,没有IF语句?并根据功能名称的名称,调用它?让我告诉你伪代码,可以更好地描述情况:
for(int I=1;I<=N;I++){
functionI();
}
Run Code Online (Sandbox Code Playgroud)
我的意思是,如果它在某种程度上可以计算(例如在char数组中,但也可以通过任何其他方式)代码,我将在稍后插入和使用.但不是字符串,而是直接像代码.让我演示一下不同的例子:
int num=3;
char functionInString[]="printf(num);
//Some code, that would for example change letters in
functionInString, for example to different fuction
consisting of letters
//And here, do whatever is written in functionToString
Run Code Online (Sandbox Code Playgroud)
对不起,如果我不够清楚.任何人都可以告诉我,如果有可能用C语言,或任何不同的语言,这个概念怎么称呼?
您可能需要了解闭包和回调是什么.函数指针很有用,但可能不够单独阅读(了解函数指针如何用于实现闭包).
你应该学习更多关于C的知识,所以阅读有关C编程的好书.我建议下载C11标准n1570并瞥一眼.一个非常重要的概念是未定义的行为,你应该害怕它.
如果它以某种方式可以计算(例如,在char数组中,但也可以通过任何其他方式)代码,我将插入并稍后使用.但不是字符串,而是直接像代码.
这在纯粹的标准C代码中是不可能的(因为包含你的程序的翻译单元的集合是固定的),并且原则上任何有效的函数指针值都应该指向一些现有的函数(因此stricto sensu调用任何其他函数指针值将是未定义的行为).然而,一些实现能够构建(在一些实施的具体方式),不知何故"有效"的新函数指针(但是这是外面的C11标准).在纯粹的哈佛架构中,代码位于ROM中,这甚至是不可能的.
但是,如果您在现代操作系统下运行(我建议使用Linux),您可以使用动态加载和插件工具.我专注于Linux(对于Windows来说,细节是非常不同的,而且细节是邪恶的;阅读Levine的书籍" Linkers and Loaders").阅读操作系统:三个简单的部分,以了解有关操作系统的更多信息.
所以你可以做的(在Linux上)是在运行时生成一些临时文件中的一些C代码,例如/tmp/generated.c; 你需要定义你对该文件的约定(例如,它定义了一个void pluginfun(int)具有一些额外的理想属性的函数); 然后你派一个编译命令把它编译成一个共享对象(读取如何通过Drepper 编写共享库),这是一个插件.所以你的程序可能运行(可能使用system(3),或者更低级别的系统调用,如fork(2),execve(2),waitpid(2)等......)编译过程,例如gcc -Wall -O -fPIC /tmp/generated.c -shared -o /tmp/generatedplugin.so; 之后,您的主程序将使用dlopen(3)加载该插件:
void* dlh = dlopen("/tmp/generatedplugin.so", RTLD_NOW);
if (!dlh) {
fprintf(stderr, "dlopen /tmp/generatedplugin.so failed: %s\n",
dlerror());
exit(EXIT_FAILURE);
}
Run Code Online (Sandbox Code Playgroud)
由于您关心函数指针,如果您将其签名声明为类型,则代码将更具可读性:
typedef void pluginfun_type(int);
Run Code Online (Sandbox Code Playgroud)
然后很容易声明一个指向该签名函数的函数指针:
pluginfun_type* fptr = NULL;
Run Code Online (Sandbox Code Playgroud)
并且您的程序可以使用dlsym(3)pluginfun插件中的函数地址:
fptr = (pluginfun_type*) dlsym(dlh, "pluginfun");
if (!fptr) {
fprintf(stderr, "dlsym of pluginfun failed: %s\n", dlerror());
exit(EXIT_FAILURE);
}
Run Code Online (Sandbox Code Playgroud)
并最后用于(*fptr)(3)调用该函数.
您应该gcc -O -Wall foo.o bar.o -rdynamic -ldl -o foobarprog用来链接您的主程序.您最后可能会调用dlclose(3),但如果您的插件中的某个函数仍有一些活动框架,则不应该这样做.
这种生成插件方法在Linux上运行得非常好.我的manydl.c是一个玩具程序,生成"随机"C代码,将生成的C代码编译成生成的插件,然后加载该插件(并且可以重复多次).它表明,在实践中,Linux程序可以生成和加载数十万(甚至数百万,如果你足够耐心)插件.所述代码段泄漏在实践中是可以接受的(并且可能通过避免小心使用的dlclose)
今天计算机速度非常快.您可以在每次REPL交互中生成一个小插件(少于一千行C),并编译并加载它(对于这么小的插件,它通常需要不到0.1秒),这实际上与人类兼容互动(我在我过时的GCC MELT项目中这样做了;我将在bismon-chariot-doc.pdf草案报告中描述的bismon项目中这样做).
要在运行时动态生成代码,您还可以使用一些JIT编译库.其中有很多,包括libgccjit,LLVM(在C++中),asmjit,libjit,GNU 闪电,tinycc及其libtcc.其中一些能够快速发出机器代码,但那时代码的性能可能不是很好.其他正在进行优化,比如一个好的C编译器(特别是里面使用GCC的libgccjit).当然,这些优化需要一些时间,因此机器代码的发送速度要慢得多,但其性能与优化的C代码一样好.
在许多情况下,脚本语言提供某种评估.一些口译员的设计很容易嵌入您的应用程序中(注意事项和注意事项),尤其是Lua或Guile(和Nim).许多解释器(Ocaml,Python,Perl,Parrot,Ruby,...)也可以以某种方式嵌入到您的应用程序中(您可能需要了解垃圾收集术语).当然,所有都需要一些编码约定.实际上,解释器比编译代码慢.
任何人都可以告诉我,如果有可能用C语言,或任何不同的语言,这个概念怎么称呼?
您可能需要阅读有关元编程,评估,多级编程,同性,反射,延续,类型内省,堆栈跟踪的更多信息(请考虑Ian Taylor的libbacktrace).
您可能对类似Lisp的语言感兴趣.首先阅读SICP,然后阅读Queinnec的书" Lisp In Small Pieces"和Scott的书" Programming Language Pragmatics".请注意,SBCL(Common Lisp实现)在每次REPL交互时都会生成机器代码.
既然你提到人工智能的兴趣,你可以看看J.Pitrat的博客.他的CAIA系统是自引导的,因此产生了其C代码的总体(接近500KLOC).