2026/5/18 21:55:55
网站建设
项目流程
品牌高端网站,简单的网站开发,湘潭网站建设 就找磐石网络,电子商务网站开发方式一、Partial application
Partial#xff0c;大家应该比较熟悉#xff0c;在模板编程中的偏特化就用这个单词#xff0c;有过python编程经验的更容易理解。Partial application#xff0c;大家可以把它称为“偏应用”或“部分应用”。这个名字听上去有点特别#xff0c;但…一、Partial applicationPartial大家应该比较熟悉在模板编程中的偏特化就用这个单词有过python编程经验的更容易理解。Partial application大家可以把它称为“偏应用”或“部分应用”。这个名字听上去有点特别但如果有模板偏特化的知识的就可以顺理成章的明白可能就是把一些未知的值给定义出来确实也是如此。在函数编程中将一个多参函数的部分参数固定来创建一个较少参数的新函数。大家可以理解为把其中的部分参数“重新”给个“默认值”。如果从数学的角度来看大家可以看作是对未知参数的一个逐步“消元”的过程。给大家一个python的例程来先入为主一下这样更容易理解fromfunctoolsimportpartial#set functiondeffunc(a,b,c,d):return100*(abcd)# call partial functionpfpartial(func,1,2,3)pf1partial(func,1,2)pf2partial(func,1)#call and printprint(pf(4))# 1000printf(pf1(3,4))#1000printf(pf2(2,3,4))#1000上面的代码通过partial接口处理有四个参数的函数func返回一个函数指针pf其中三个参数已经固定为1,2,3(pf1,pf2类似)这个函数指针只需接受一个参数即可。二、模板元编程实现在上面的分析说明和python的代码展示中应该可以很明了的知道什么是Partial application。那么就要引入到C的模板编程中那么在模板编程中如何实现Partial application的应用呢在变参模板函数的应用是不是也是这样一个个的固定参数然后不断的递归处理那么它也可以改造后应用在Partial application开发中。看下面的例子#includeiostream#includetupletemplatetypename Func,typename...PartialArgsclass Partial__{private:Func f_;std::tuplePartialArgs...pArgs_;public:Partial__(Func func,PartialArgs...args):f_(func),pArgs_(args...){}//std::index_sequence_for是 C14引入的一个模板别名用于根据给定的类型参数包生成一个 std::index_sequence其索引数量等于参数包中类型的个数。//例如std::index_sequence_forT1, T2, T3 等价于std::index_sequence0, 1, 2templatetypename...RestArgsautooperator()(RestArgs...rArgs)const{returnpartialImpl(std::index_sequence_forPartialArgs...{},rArgs...);}private:templatesize_t...Id,typename...RestArgsautopartialImpl(std::index_sequenceId...,RestArgs...rArgs)const{returnf_(std::getId(pArgs_)...,rArgs...);}};templatetypename Func,typename...PartialArgsautopartial(Func func,PartialArgs...args){returnPartial__Func,PartialArgs...(func,args...);}intmulSum(inta,intb,intc,intd){return10*(abcd);}intmain(){autopf2partial(mulSum,1,2);std::coutcall pf2 result: pf2(3,4)std::endl;autopf3partial(mulSum,1,2,3);std::coutcall pf3 result:pf3(4)std::endl;return0;}也可以模仿python实现使用函数指针C中最常用的当然是lambda表达式#includeiostreamintsum(inta,intb,intc,intd){returnabcd;}autocreateMul(inta){return[a](intb){returna*b;};}intmain(){//单参数处理autopSum[](intb,intc,intd){returnsum(1,b,c,d);};std::coutpSum ret: pSum(2,3,4)std::endl;//通用autopartial[](autofunc,auto...partialArgs){return[func,partialArgs...](auto...rArgs){returnfunc(partialArgs...,rArgs...);};};autopSum1partial(sum,1);std::coutpSum1 ret: pSum1(2,3,4)std::endl;//return lambdastd::coutreturn lambda ,ret:createMul(2)(5)std::endl;return0;}如果在C20以上也可以使用Lambda表达式中的模板参数包展开机制#includeiostream#includeutilitytemplatetypename F,typename...ArgsautopartialImplFunc(Ff,Args...allArgs){//Lambda表达式捕获列表中展开参数包return[fstd::forwardF(f),...allArgsstd::forwardArgs(allArgs)](auto...rArgs)mutable{returnf(allArgs...,std::forwarddecltype(rArgs)(rArgs)...);};}intmultiply(inta,intb,intc,intd){returna*b*c*d;}intmain(){autofuncpartialImplFunc(multiply,1,2);std::coutfunc(3,4)std::endl;return0;}如果在C23的环境中则可以使用this指针和std::bind_front接口class multiply{public:templatetypename Mautooperator()(this Mmyself,inta,intb){returna*b*myself.num_;}intnum_1;};voidtestCpp23(){multiply demo{10};autofBindstd::bind_front(multiply::operator(),demo,10);std::coutfBind ret : fBind(10)std::endl;}上面的代码在前面的文章中都有过分析包括std::index_sequence等对C20中的用法也在注释中进行了说明如果不明白大家可参看相关的技术文档即可。另外在元编程的应用中偏特化从某种角度来看也算是一种Partial application应用。另外也可以使用std::bind函数来模拟实现功能不过它看起来还是有些与python的实现或者说正常的实现有些不一样。三、分析说明Partial application应用一个特点是惰性加载或者说延迟计算意思就是直到函数最终的调用时才会被执行。正如上面的例子它可以不断的变换参数的数量自由的创建函数的变体。即可以理解为创建多个有默认参数的别名同实现函数也可以认为创建多个有重载意义的不同名函数。这种用法在普通函数编程中确实意义没有多大更多还是应用于模板的元编程中。四、应用场景新技术的应用无非就是为了“偷懒”或解决现有技术无法方便解决的问题Partial application出现也是如此。其应用的场景主要有配置参数和验证处理从上面的应用可以发现在项目的一些配置选项及验证或数据库操作中的参数处理中它有着广泛的应用可能数据计算和数据处理这种应用就更加常见了比如过滤某些特殊的参数的数据其它都使用默认参数只有重点的参数过滤计算中的不同情况的计算类似于重载等等算法控制这个和数据计算有些类似通过不同的参数实现不同的算法策略动态任务管理可以实现动态的参数配置来达到不同的任务生成从而在设计模式、并行编程以及其它一些相应场景下应用五、例程下面给出一个简单的例程用来处理不同的任务#includefunctional#includeiostream#includemap#includestringclass TaskWrap{public:using Taskstd::functionvoid(TaskWrap,TaskWrap);voidaddTask(conststd::stringid,Task task){tasks_[id]task;}voidrunTask(conststd::stringid,TaskWraptw){if(tasks_.find(id)!tasks_.end()){tasks_[id](*this,tw);}}private:std::mapstd::string,Tasktasks_;};class TaskGenerator{public:// create task1autocreateTask1(intsign,intowner){return[sign,owner](TaskWraptw1,TaskWraptw2){std::coutrun task1 sign:signstd::endl;};}// create task2autocreateTask2(intsign,intowner){return[sign,owner](TaskWraptw1,TaskWraptw2){std::coutrun task2 sign:signstd::endl;};}// create task3autocreateTask3(conststd::stringid,intt1,intt2){return[id,t1,t2](TaskWraptw1,TaskWraptw2){std::coutrun task3 id:id and run t1 - t2std::endl;};}};intmain(){TaskGenerator tg;TaskWrap runner;TaskWrap worker;autohBrushtg.createTask1(1,2);autohWashtg.createTask2(5,6);autohDresstg.createTask3(dress,7,8);runner.addTask(brush,hBrush);runner.addTask(wash,hWash);runner.addTask(dress,hDress);runner.runTask(brush,worker);runner.runTask(wash,runner);runner.runTask(dress,runner);return0;}六、总结古人经常说“天下文章一大抄”这开发语言之间其实也是类似。都是发现某些问题在当前语言无法方便快捷的处理的情况下又创建了一个新的语言。C的特点就在于其高度的底层性和灵活性可以模拟实现各种具体的高级语言的特性这本来是它的优势但为了实现这些特性其过程变得相当复杂。这反而让很多开发者认为C难于掌握正所谓优势即劣势这就是事物的两个方面。