什么样的语言特性适合于只有几行不同的类似函数

what kind of language feature is suitable for similar functions with only several different lines?

本文关键字:几行 函数 语言 适合于 什么样      更新时间:2023-10-16

我有一个编程问题,我想了好几天,但仍然没有好的解决方案。

有几个又大又长又相似的函数,其中只有几行不同。例如:

    void bigfunc(){
       //blabla..
       double i, j, k;
       double result;
      //only this part is different
      { result=i++; }              //in bigfunc1;
      { result=i*cos(j);}          //in bigfunc2;
      { result=sin(i)*cos(j)*tan(k);}     //in bigfunc3;
      //blabla...
    }

最简单的方法是复制bigfunction 3次,然后分别编辑不同的行。显然,这不是一个好主意。

现在,我选择c++中的模板函数方法,它将不同的行包含为几个小函数,例如:

    < template class F >
     void bigfunc(F& f){
      ...
      f(i,j,k); //call the function.
      ...
    }
      void f1(int i, int j, int k){
          i++; //only use parameter i!
      }  
      void f2(int i, int j, int k){
          i++; j++; //only use parameter i and j!
      }  
      void f3(int i, int j, int k){
          i++; j++; j++;
      }  

但是,我们必须统一f(int, int, int)的原型,以便在三个不同的bigfuncs中包含所有的输入参数,而实际上,以bigfunc1为例,实际上只需要f(int i)。因此,这个方法似乎不够优雅。

我想如果有一些抽象的机制可以把句子块作为函数参数或者模板函数参数。那太棒了!例如(伪代码):

       < template class Code>
       void bigfunc(Code code){
        //...
         code(); //direct expand the code in place, which will be amazingly beautiful.
        //....
       }
       code1(){
            i++;   //just be care for the variable name, like #include
       }
       ....

在任何语言中,是否有其他的编译时抽象机制,可以优雅地解决这样的问题?

到目前为止,我只听说D语言的static_if也许可以处理这种情况。例如:
      < template class F>
      void bigfunc(F f){
           if( static_if(f) == func1 ) //it is justified at compile-time, not at run-time.
              i++;
           else if ( static_if(f)==func2){
               i++; j++;
           }...
      }

很抱歉,我不知道D,这只是为了解释。

谢谢你的建议!

在d中有几种不同的方法:

void bigfunc(string code)(){
   //blabla..
   double i, j, k;
   double result;
  //only this part is different
  mixin(code);
  //blabla...
}
alias bigfunc!"{ result=i++; }" bigfunc1;
alias bigfunc!"{ result=i*cos(j); }" bigfunc2;
alias bigfunc!"{ result=sin(i)*cos(j)*tan(k);}" bigfunc3;

这类似于C宏的方式-你传递一个字符串给模板(第一组参数是编译时模板参数,第二组是常规函数参数),然后作为代码混合创建函数。

如果足够容易的话,你也可以使用static。

void bigfunc(int version)(){
   //blabla..
   double i, j, k;
   double result;
    static if(version == 1)
          { result=i++; }
    else static if(version == 2)
// well you get the idea

然后你可以将不同的模板参数别名为新的名称,就像使用字符串一样。

你可以只用一个宏:

#define CREATE_FUNC(name, code)    
void name(){                       
   //blabla..                      
   int i, j, k;                    
                                   
  //only this part is different    
  code                             
                                   
  //blabla...                      
}
CREATE_FUNC(bigfunc1, { i++; });
CREATE_FUNC(bigfunc2, { i++; j++; });
CREATE_FUNC(bigfunc3, { i++; j++; k++; });

不那么漂亮,但是"c + + + "

您正在寻找的神秘编程语言特性称为"子程序":

void bigfunc(int variant){
   //blabla..
   double i, j, k;
   double result;
  //only this part is different
  switch (variant) {
  case 0: { result=i++; } break;             //in bigfunc1;
  case 1: { result=i*cos(j);} break;          //in bigfunc2;
  case 2: { result=sin(i)*cos(j)*tan(k);} break;     //in bigfunc3;
  }
  //blabla...
}
void bigfuncA() { bigfunc(0); }
void bigfuncB() { bigfunc(1); } 
void bigfuncC() { bigfunc(2); }

我不太确定这是你想要的,但是函数对象可能会达到这个目的。

template<typename Func, typename... Args>
void bigfunc(Func f, Args... args) {
  //blahblah
  f(args...);
  //blahblah
}
struct f1 {
    void operator()(int& i) {
        ++i;
    }
};
struct f2 {
    void operator()(int& i, int& j) {
        f1()(i);
        ++j;
    }
};
struct f3 {
    void operator()(int& i, int& j, int& k) {
        f2()(i, j);
        ++k;
    }
};
//main...
int i, j, k;
//...
bigfunc(f3(), i, j, k);

为什么不把'公共代码'放在一个内联函数中,然后由所有三个函数调用?

这很容易通过编写单个高阶函数(HOF)来解决,这是函数式编程的范例。一个常见的用例是打开一个文件,对它做一些事情,然后再次关闭它。传统且容易出错的方法是手动执行此操作。hof提供了一个优雅的替代方案:

void *withFile(const char *fp, const char *mode, void *(*k)(FILE *)) {
    FILE *fh;
    void *result;
    fh = fopen(fp, mode);
    if (fh == NULL) return NULL;
    result = k(fh);
    fclose(fh);
    return result;
}

现在您可以编写几个只处理实际文件读取的小函数。withFile过程确保文件始终处于关闭状态。要更深入地解释这个范例,您可以阅读我以前的博客文章。

注意,user函数并不关心它是否被传递了额外的参数。请忽略user函数中的额外参数

您的第一个模板方法看起来并不太可怕,除非分析证明不是这样,否则编译器很可能会优化掉不需要的函数和未使用的变量,特别是如果您在f1和f2的声明中没有给出它们的名称。

但是为了概括事情,你需要退后一步,以抽象的方式思考你实际想要做什么,而不是在具体情况下你是如何做的。i j k到底是什么意思?您有一组计数器变量,并且在您的bigfunction中的某一点上,您希望增加所有计数器变量:

template <int N>
struct Counting_Variables;
template <>
struct Counting_Variables<1> {
    int i;
};
template <>
struct Counting_Variables<2> {
    int i, j;
};
template <>
struct Counting_Variables<3> {
    int i,j,k;
};
void increase_counters ( Counting_Variables<1> & arguments ) {
    ++(arguments.i);
}
void increase_counters ( Counting_Variables<2> & arguments ) {
    ++(arguments.i);
    ++(arguments.j);
}
void increase_counters ( Counting_Variables<3> & arguments ) {
    ++(arguments.i);
    ++(arguments.j);
    ++(arguments.k);
}
void bigfunction () {
    // Enter appropriate number of counter variables as template argument here:
    Counting_Variables<2> counters;
    // Be lazy and introduce shortcuts for counters.i etc.
    int & i = counters.i;
    int & j = counters.j;

    // do some stuff here
    // increase all the counters
    increase_counters( counters );
    // do more stuff here
};

我是有罪的,使Counting_Variables成为模板并不是严格必要的。将它们命名为Counting_Variables_with_i_and_j而不是N=2具有相同的效果。