针对不同结构的迭代器?

时间:2016-06-09 09:23:35

标签: c algorithm design-patterns

如果我们有多个数据结构,例如:

typedef struct {
   int par1;
   //...
   int parn1;
} struct1;

typedef struct {
   int par1;
   //...
   int parn2;
} struct2;

(通常考虑字段类型可能不同)是否有意义抽象出这种结构字段的迭代器?

这实际上是在实践中使用的吗?

我想到的是(或多或少伪代码):

typedef struct {
   int par1;
   //...
   int parn1;
} struct1;

typedef struct {
   int par1;
   //...
   int parn2;
} struct2;

typedef struct {
   int* iterator_ptr;
   int curr_pos;
   int n_field;
} struct_iterator;

typedef union {
   struct1 s1;
   struct2 s2;
} generic_struct;

typedef enum {
   label_struct_1,
   label_struct_2,
   n_struct
} struct_label;

struct_iterator get_iterator(generic_struct* s,struct_label label) {
   struct_iterator it;
   if(label == label_struct_1) {
      it.iterator_ptr = s->par1;
      it.curr_pos = 0;
      it.n_fields = n1;
   } else if(label == label_struct_2) {
      it.iterator_ptr = s->par1;
      it.curr_pos = 0;
      it.n_fields = n2;
   } else {
      //something else, maybe other structures to handle
   }
   return it;
 }

目的是基本上进行代码重构,我在C中有一个可怕的代码,我想让它更易读,更容易理解。我有上面展示的那些结构,以及在这种结构上运行的不同算法,高级算法实际上是相同的,但是不是编写带有专门的斑点的通用版本,而是直接写出专门版本的这样的算法。在这种算法的步骤中,有一个遍历结构参数的迭代,因为这些结构可能在字段数量上有所不同,但在某些情况下类型是相同的,我想到的是抽象试图实现迭代器的概念

我理解这可能听起来不必要,但我想要实现的是最简单的代码重用方式。像“如果你想扩展代码只是通过这些操作来实现这个结构,你不需要担心其他事情”。

我提出的例子是为了清楚起见,它不是真正的代码,我想知道这个想法是否有意义。

代码是用C语言编写的,我所谈论的大部分概念都是用C ++实现的,但我不得不使用C语言。

实际运作的代码:

文件data_structure_algorithm_1.h:

#ifndef HEADER_1_H
#define HEADER_1_H
#include <stdio.h>
#include <stdlib.h>

typedef struct {
   int data_1;
   int data_2;
   int data_3;
} struct_1;

void operation_1_struct_1(struct_1* s, int field) {
   if(field == 0) {
      s->data_1 = 1;
   } else if(field == 1) {
      s->data_2 = s->data_1*2 + 3;
   } else {
      s->data_3 = (s->data_1 + s->data_2)/2;
   }
}

void operation_2_struct_1(struct_1* s) {
   printf("s->data_1 = %d\n",s->data_1);
   printf("s->data_2 = %d\n",s->data_2);
   printf("s->data_3 = %d\n",s->data_3);
}

data_structure_algorithm_2.h:

#ifndef HEADER_2_H
#define HEADER_2_H
#include <stdio.h>
#include <stdlib.h>

typedef struct {
   int data_1;
   int data_2;
   int data_3;
   int data_4;
   int data_5;
   int data_6;
} struct_2; 

void operation_1_struct_2(struct_2* s, int field) {
   if(field == 0) {
      s->data_1 = 1;
   } else if(field == 1) {
      s->data_2 = s->data_1 - 3;
   } else if(field == 2) {
      s->data_3 = (s->data_1 - s->data_2)/2;
   } else if(field == 3) {
      s->data_4 = s->data_3 - s->data_2;
   } else {
      s->data_5 = 1;
      s->data_6 = 9;
   }
}

void operation_2_struct_2(struct_2* s) {
   printf("s->data_1 = %d\n",s->data_1);
   printf("s->data_2 = %d\n",s->data_2);
   printf("s->data_3 = %d\n",s->data_3);
   printf("s->data_4 = %d\n",s->data_4);
   printf("s->data_5 = %d\n",s->data_5);
   printf("s->data_6 = %d\n",s->data_6);
}

#endif

iterator.h:

#ifndef HEADER_3_H
#define HEADER_3_H
#include "data_structure_algorithm_1.h"
#include "data_structure_algorithm_2.h" 

typedef enum {
   label_struct_1,
   label_struct_2,
   n_struct } label_struct;

typedef union {
   struct_1 s1;
   struct_2 s2;
} generic_struct;

void operation_1_struct(generic_struct *s, int index, label_struct label) {
   switch(label) {
      case label_struct_1: {
         operation_1_struct_1(&(s->s1),index);
         break;
      }
      case label_struct_2: {
         operation_1_struct_2(&(s->s2),index);
         break;
      }
   }
}

void operation_2_struct(generic_struct *s, label_struct label) {
   switch(label) {
      case label_struct_1: {
         operation_2_struct_1(&(s->s1));
         break;
      }
      case label_struct_2: {
         operation_2_struct_2(&(s->s2));
         break;
      }
   }
}

typedef struct {
   int *iterator_ptr;
   int curr_pos;
   int size;
} iterator;

iterator get_iterator_s1(struct_1* s1) {
   iterator it;
   it.iterator_ptr = &(s1->data_1);
   it.curr_pos = 0;
   it.size = 3;
   return it;
}

iterator get_iterator_s2(struct_2 *s2) {
   iterator it;
   it.iterator_ptr = &(s2->data_1);
   it.curr_pos = 0;
   it.size = 6;
   return it;
}

iterator get_iterator(generic_struct* s, label_struct label) {
   switch(label) {
      case label_struct_1: {
         return get_iterator_s1(&(s->s1));
         break;
      }
      case label_struct_2: {
         return get_iterator_s2(&(s->s2));
         break;
      }
   }
}

//I don't want to modify this, because this function could be huge
void algorithm(generic_struct* s, label_struct label) {
   iterator it;

   it = get_iterator(s,label);

   while(it.curr_pos != it.size) {
      operation_1_struct(s,it.curr_pos,label);
      it.curr_pos++;
   }

   operation_2_struct(s,label);
}

main.c中:

   #include "iterator.h"
   #include <string.h>
   #include <assert.h>

   int main(int argc, char** argv) {

   generic_struct strct;
   label_struct ls;
   int choice;

   assert(argc <= 2);

   if(argc == 1) {
      while(1) {
         printf("Choose the test you'd like to perform:\n");
         printf("1. struct_1;\n");
         printf("2. struct_2;\n");
         printf("Choice: "); scanf("%d",&choice);
         if(choice < 1 || choice > 2) {
            printf("Choice not valid... please try again\n");
         } else {
            if(choice == 1) ls = label_struct_1;
            else ls = label_struct_2;
            break;
         }
      }
   }
   else {
      if(strcmp(argv[1],"struct_1") == 0) {
         ls = label_struct_1;
      } else if(strcmp(argv[1],"struct_2") == 0) {
         ls = label_struct_2;
      }
   }

   algorithm(&strct,ls);

   return 0;
}

我现在想让程序“算法”在具有类似行为的结构上工作我只需添加一个类似的文件“data_structure_algorithm_3.h”并更新联合,并切换语句。

请记住,这是一个非常小的例子(它可能是一种小框架)。

我试图理解的是天气与否这种方法可以在更大的背景下有任何好处(试图想象我的功能更复杂,程序算法“巨大”调用所有这些类似的算法“,它是否更方便重写特殊版本的“算法”或写几个算法调用的小函数?

我希望这次能说明我的观点。

更新

从网络资源中获取灵感,以及其他一些答案,您如何看待以下内容?:

base.h:

#ifndef BASE_H_
#define BASE_H_

typedef struct base_struct base_struct;

typedef struct {
    int *ptr;
    int curr;
    int size;
} iterator;

typedef struct {
    void (*operation_1)(struct base_struct* b, int index);
    void (*operation_2)(struct base_struct* b);
    iterator (*get_iterator)(struct base_struct* b);
} vtable;

struct base_struct {
    vtable* vtbl;
};

void procedure_1(base_struct *b, int index) {
    b->vtbl->operation_1(b, index);
}

void procedure_2(base_struct *b) {
    b->vtbl->operation_2(b);
}

iterator get_iterator_proc(base_struct* b) {
    return b->vtbl->get_iterator(b);
}

void algorithm(base_struct* s) {
    iterator it;

    it = get_iterator_proc(s);

    while (it.curr != it.size) {
        procedure_1(s, it.curr);
        it.curr++;
    }

    procedure_2(s);
}


#endif /* BASE_H_ */

data_structure_1.h:

#ifndef HEADER_1_H
#define HEADER_1_H
#include "base.h"
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    base_struct base;
    int data_1;
    int data_2;
    int data_3;
} struct_1;

void operation_1_struct_1(base_struct* b, int field) {
    struct_1* s = (struct_1*)b;
    if (field == 0) {
        s->data_1 = 1;
    } else if (field == 1) {
        s->data_2 = s->data_1 * 2 + 3;
    } else {
        s->data_3 = (s->data_1 + s->data_2) / 2;
    }
}

void operation_2_struct_1(base_struct* b) {
    struct_1* s = (struct_1*)b;
    printf("s->data_1 = %d\n", s->data_1);
    printf("s->data_2 = %d\n", s->data_2);
    printf("s->data_3 = %d\n", s->data_3);
}

iterator get_iterator_struct_1(base_struct* b) {
    iterator it;
    struct_1 *s = (struct_1*)b;
    it.ptr = &(s->data_1);
    it.curr = 0;
    it.size = 3;
    return it;
}

vtable vtbl_struct_1 = {&operation_1_struct_1,&operation_2_struct_1,&get_iterator_struct_1};

void init_struct_1(struct_1* s) {
    s->base.vtbl = &vtbl_struct_1;
}

#endif /* DATA_STRUCTURE_1_H_ */

data_structure_2.h

#ifndef HEADER_2_H
#define HEADER_2_H
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    base_struct base;
    int data_1;
    int data_2;
    int data_3;
    int data_4;
    int data_5;
    int data_6;
} struct_2;

void operation_1_struct_2(base_struct* b, int field) {
    struct_2 *s = (struct_2*)b;
    if (field == 0) {
        s->data_1 = 1;
    } else if (field == 1) {
        s->data_2 = s->data_1 - 3;
    } else if (field == 2) {
        s->data_3 = (s->data_1 - s->data_2) / 2;
    } else if (field == 3) {
        s->data_4 = s->data_3 - s->data_2;
    } else {
        s->data_5 = 1;
        s->data_6 = 9;
    }
}

void operation_2_struct_2(base_struct* b) {
    struct_2 *s = (struct_2*)b;
    printf("s->data_1 = %d\n", s->data_1);
    printf("s->data_2 = %d\n", s->data_2);
    printf("s->data_3 = %d\n", s->data_3);
    printf("s->data_4 = %d\n", s->data_4);
    printf("s->data_5 = %d\n", s->data_5);
    printf("s->data_6 = %d\n", s->data_6);
}

iterator get_iterator_struct_2(base_struct* b) {
    iterator it;
    struct_1 *s = (struct_1*)b;
    it.ptr = &(s->data_1);
    it.curr = 0;
    it.size = 6;
    return it;
}

vtable vtbl_struct_2 = {&operation_1_struct_2,&operation_2_struct_2,&get_iterator_struct_2};

void init_struct_2(struct_2* s) {
    s->base.vtbl = &vtbl_struct_2;
}

#endif /* DATA_STRUCTURE_2_H_ */

的main.c

#include "data_structure_1.h"
#include "data_structure_2.h"
#include <string.h>
#include <assert.h>

int main(int argc, char** argv) {

    struct_1 s1;
    struct_2 s2;
    init_struct_1(&s1);
    init_struct_2(&s2);

    printf("test struct 1\n");
    algorithm((base_struct*)(&s1));

    printf("test struct 2\n");
    algorithm((base_struct*)(&s2));

    return 0;
}

它基本上为虚拟功能实现了一个小的虚拟表。这对我的目的应该足够好吗?我基本上只是创建不同的结构,它们是函数表,我不需要修改任何其他内容,我错了吗?

1 个答案:

答案 0 :(得分:2)

所有这些都取决于数据的性质。实现抽象层可能有意义也可能没有意义。

如果制作一个抽象层是有意义的,我无法用有限的数据信息告诉它,它应该以更优雅的方式完成。

例如,对于每个结构,您可以定义一个知道如何访问该结构的函数。

然后你可以这样做:

int get_struct1_iterator (struct1* s);
int get_struct2_iterator (struct2* s);
...

#define get_iterator(s)  _Generic((s), \
  struct1: get_struct1_iterator, \
  struct2: get_struct2_iterator)(&s)

int* i = get_iterator(my_struct);

或者你可以根据标准化的函数格式设计一些东西,让每个struct包含一个函数指针,但是你必须改变结构。