回调函数

回调函数

   回调函数  
    回调函数是一个程序员不能显式调用的函数;通过将回调函数的地址传给调用者从而实现调用。回调函数使用是必要的,在我们想通过一个统一接口实现不同的内容,这时用回掉函数非常合适。比如,我们为几个不同的设备分别写了不同的显示函数: void TVshow(); void ComputerShow(); void NoteBookShow()...  等等。这是我们想用一个统一的显示函数,我们这时就可以用回掉函数了。 void show(void (*ptr)()); 使用时根据所传入的参数不同而调用不同的回调函数。    

不同的编程语言可能有不同的语法,下面举一个 c 语言中回调函数的例子,其中一个回调函数不带参数,另一个回调函数带参数。

#include <stdlib.h>
#include <stdio.h>
int Test1()
{
     int i;
      for(i=0; i<30; i++)
      {
        printf("The %d th charactor is: %c\n", i, (char)('a' + i%26));

}
      return 0;
}

int Test2(int num)
{
       int i;

for (i=0; i<num; i++)

{
       printf("The %d th charactor is: %c\n", i, (char)('a' + i%26));
      }
       return 0;

}
void Caller1( void (*ptr)() )// 指向函数的指针作函数参数  
{
  (* ptr)();
}  
void Caller2(int n, int (*ptr)())// 指向函数的指针作函数参数   ,  这里第一个参数是为指向函数的指针服务的,  
{
   // 不能写成   void Caller2(int (*ptr)(int n))  ,这样的定义语法错误。    (* ptr)(n);
}

int main()
{
       printf("************************\n");

Caller1(Test1); //相当于调用 Test1();
      printf("&&&&&&************************\n");
       Caller2(30, Test2); //相当于调用 Test2(30);

return 0;
}
  以上通过将回调函数的地址传给调用者从而实现调用,但是需要注意的是带参回调函数的用法。要实现回调,必须首先定义函数指针。函数指针的定义这里稍微提一下。比如:
     int (*ptr)();  这里 ptr  是一个函数指针,其中  (*ptr) 的括号不能省略,因为括号的优先级高于星号,那样就成了一个返回类型为整型的函数声明了。  
 
   
    // 例子2.    #include <stdlib.h>   #include <stdio.h>  
    #define METHOD  1  
    int Test1(void)    {         int i;         for(i=0; i<5; i++)         {              printf("The %d th charactor is: %c\n", i, (char)('a' + i%26));         }         return 0;    }  
    int Test2(int num)    {         int i;         for (i=0; i<num; i++)         {              printf("The %d th charactor is: %c\n", i, (char)('a' + i%26));         }         return 0;    }  
   
    #ifdef METHOD    typedef int (* callback1)(void);  
    //typedef int (* callback2)(int); 这个声明也是ok 的..     typedef int (* callback2)(int num);  
    void Caller1( callback1 ptr )//指向函数的指针作函数参数    {         printf("method 1------caller1............\n");         ptr();    }  
    void Caller2(int n, callback2 ptr)//指向函数的指针作函数参数,这里第一个参数是为指向函数的指针服务的,    {         printf("method 1------caller1............\n");         ptr(n);    }  
    #else  
    void Caller1( int (*ptr)() )//指向函数的指针作函数参数    {         printf("method 2------caller1............\n");         (*ptr)();    }  
    //以下三种方式定义都ok !!!!    //void Caller2(int n, int (*ptr)() )    //void Caller2(int n, int (*ptr)(int) )    void Caller2(int n, int (*ptr)(int num) )//指向函数的指针作函数参数,这里第一个参数是为指向函数的指针服务的,    {         printf("method 2------caller2............\n");         //不能写成void Caller2(int (*ptr)(int n)),这样的定义语法错误。         (*ptr)(n);    }  
    #endif  
    int main(void)    {         printf("\n\n");         printf("************************\n");         Caller1(Test1); //相当于调用Test1();         printf("\n\n\n");                  printf("&&&&&&************************\n");         Caller2(8, Test2); //相当于调用Test2(30);         printf("\n\n");                  return 0;    }  
   
   // 实例3    #include<stdio.h>  
    // 方法指针的格式为:int (*ptr)(char *p) 即:返回值(指针名)(参数列表)    typedef int (*CallBackFun)(char *p); // 为回调函数命名,类型命名为 CallBackFun,参数为char *p  
    int Afun(char *p) {    // 方法 Afun,格式符合 CallBackFun 的格式,因此可以看作是一个 CallBackFun        printf("Afun ,callback print the string is :%s!\n", p);        return 0;    }  
    int Cfun(char *p) {    // 方法 Bfun,格式符合 CallBackFun 的格式,因此可以看作是一个 CallBackFun        printf("Cfun ,callback print the string is :%s, Nice to meet you!\n", p);        return 0;    }  
    int call(CallBackFun pCallBack, char *p) { // 执行回调函数,方式一:通过命名方式        printf("call method 1, the string is :%s!\n", p);        pCallBack(p);        return 0;    }  
    // int call2(char *p, int (*ptr)(char *p))     int call2(char *p, int (*ptr)()) { // 执行回调函数,方式二:直接通过方法指针        printf("=======call method 2, the string is :=======\n", p);         (*ptr)(p);     }  
    int main() {      
        char *p = "hello";  
        call(Afun, p);        call(Cfun, p);        printf("\n");        printf("\n");  
        call2(p, Afun);        call2(p, Cfun);        printf("\n");        printf("\n");                       // int i = getchar();        // printf("Input: %c \n", i);  
        return 0;    }    
 
   

Read more

使用本地大模型调用代码,根本就是一场骗局!

使用本地大模型调用代码,根本就是一场骗局!

通过大模型调用其他工具到底可不可行?ChatGPT 或许能轻松搞定一切,但同样的需求落在本地大模型上,恐怕就要打个问号了。 法国开发工程师 Emilien Lancelot 尝试了多款号称具备工具调用功能的 agent 框架,来看看本地大模型到底能不能完成任务,但结果就像他总结的“一无所获”。是什么让这位工程师失望了? 用 AutoGPT,得会点“糊弄学” AutoGPT 是款貌似强大的框架,提供很酷的 CLI 外加 Flutter UI,能够通过浏览器创建 agent。其主要功能是处理用户的文档、音频、视频等本地内容。 但是……它主要依靠 ChatGPT 或其他专有大模型服务来完成繁重工作,至少给我们的感觉是如此。 我们必须“唬弄”AutoGPT 才能使用 Ollama 端点,让其误认为是 ChatGPT。 ## OPENAI_API_KEY - OpenAI API

By Ne0inhk
详细说明Encoder-only架构的特点以及他和encoder decoder的区别和联系

详细说明Encoder-only架构的特点以及他和encoder decoder的区别和联系

Poe Encoder-only 架构的特点 Encoder-only 模型只使用 Transformer 的编码器部分,专注于对输入序列的理解和表征学习。这类模型通常被用在需要对输入文本进行深度理解的任务上,如文本分类、问答和语义匹配。以下是 Encoder-only 架构的核心特点: 1. 双向上下文建模 * 特点:Encoder-only 模型通过自注意力机制(Self-Attention)同时关注输入序列的前后文。 * 优势:相比单向模型(如 Decoder-only),它可以更全面地捕捉输入序列的全局语义,适合需要理解复杂上下文的任务。 * 实现方式:在训练过程中,不对输入序列进行因果掩码(Causal Masking),允许模型在任何位置访问序列的所有位置。 * 例子:BERT 的 Masked Language Model(MLM)训练任务通过随机遮盖部分单词,依赖左侧和右侧的信息来预测被遮盖的词,即双向建模的典型体现。 2. 适用于理解任务 * 特点:Encoder-only 模型专注于理解输入序列,而不生成输出序列,因此适合处理分类、

By Ne0inhk
手把手教学,DeepSeek-R1微调全流程拆解

手把手教学,DeepSeek-R1微调全流程拆解

手把手教学,DeepSeek-R1微调全流程拆解 原创 极客见识  2025年02月09日 09:02 广东 DeepSeek 通过发布其开源推理模型 DeepSeek-R1 颠覆了 AI 格局,该模型使用创新的强化学习技术,以极低的成本提供与 OpenAI 的 o1 相当的性能。 更令人印象深刻的是,DeepSeek 已将其推理能力提炼成几个较小的模型。这篇文章,我们将使用其蒸馏版本之一引导大家完成 DeepSeek-R1 的整个微调过程。 本文章将演示了如何微调其中一个模型(使用我们自己的自定义思维链数据集),然后保存和部署微调后的模型。 高级推理模型微调 DeepSeek 简介 DeepSeek-R1 是由深度求索(DeepSeek)公司开发的突破性推理模型。DeepSeek-R1 基于 DeepSeek-V3-Base(总共 671B 个参数,每次推理 37B 处于活动状态)构建,使用强化学习 (RL) 在提供最终答案之前生成思路链

By Ne0inhk
LLM o1 中的蒙特卡洛树搜索算法,DeepSeek论文中提到

LLM o1 中的蒙特卡洛树搜索算法,DeepSeek论文中提到

LLM o1 中的蒙特卡洛树搜索算法,DeepSeek论文中提到 凌青羽  2025年02月09日 09:30 上海 蒙特卡洛树搜索算法的核心是:选择与模拟。 蒙特卡洛树搜索算法的主要目标是:给定一个游戏状态来选择最佳的下一步。 前言 在讲解蒙特卡罗树算法之前,我们先玩一个“赌博”游戏。多臂老虎机(Multi-Armed Bandits)。 多臂老虎机(Multi-Armed Bandits) 游戏规则如下:赌博机有K个摇臂,每次摇动其中的任意一个摇臂,赌博机都会随机吐出一些硬币。现在允许你摇T次,请问如何尝试使得收益最大化。(有限次数的尝试的收益最大化) 思考一下,你会如何尝试?(是下面的玩法中的哪一种类型呢?) * • 纯随机(Random):每次随机选一个摇臂进行摇动。 * • 劣势:能算个期望收益,但收益不是最大的。 * • 仅探索(Exploration-only):每个摇臂摇动T/K次。 * • 相当于每个摇臂摇动的次数都一样。(劣势:浪费次数在收益较差的摇臂上) * • 仅利用(Exploitation-only):

By Ne0inhk