C словарь / карта - PullRequest
       35

C словарь / карта

0 голосов
/ 12 мая 2011

Я хочу отобразить элементы структуры, чтобы я мог исключить, если ветви в цикле.Каков наилучший способ или соглашение для реализации этого в C?Я полагаю, это может быть двумерный массив ... тогда я мог бы сопоставить целые числа с ключами символов?

    char chunk[32];
    int n;
    int i;
    char *ptr = config;
    while (*ptr != '\0') {
        int items_read = sscanf(ptr, "%31[^;]%n", chunk, &n);

        if(chunk[0] == 'S' && chunk[1] == 'P') {
            for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) {
                theMeas[i].signal_path = atoi(&chunk[2]);
            }
        }    
        if(chunk[0] == 'T' && chunk[1] == 'L') {
            for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) {
                theMeas[i].trace_length = atoi(&chunk[2]);
            }
        }    
        if(chunk[0] == 'S' && chunk[1] == 'R') {
            for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) {
                theMeas[i].sample_rate = atoi(&chunk[2]);
            }
        }   

        chunk[0]='\0';
        if (items_read == 1)
            ptr += n;
        if ( *ptr != ';' ) {
            break;
        }
        ++ptr;
    }

Ответы [ 5 ]

3 голосов
/ 12 мая 2011

Я подозреваю, что вам (в идеале) нужен словарь:

theMeas[i]["signal_path"] = atoi(&chunk[2]);

Конечно, приведенный выше синтаксис никогда не произойдет в C, но это не очень важно здесь. Проблема в том, что вам придется писать весь код, реализующий тип данных словаря, и я подозреваю, что это излишне.

Так что я подозреваю, что вы (действительно) хотите иметь способ иметь имена, которые можно использовать в цикле:

foreach(signal_path, trace_length, sample_rate)

И я здесь, чтобы сказать вам, что вы можете сделать это (вроде)! Самый простой способ - с enum:

enum fields {
  signal_path,
  trace_length,
  sample_rate,
  END_fields,
  UNKNOWN_fields,
  BEGIN_fields = 0,
};

Вместо struct членов вы используете массив:

int theMeas[size][END_fields];

Чтобы проиндексировать «член», используйте это:

theMeas[i][signal_path];

Вы можете перебрать все «члены», вы можете использовать это:

for(enum fields j = BEGIN_fields; j != END_fields; j++)
    theMeas[i][j];

Это немного ломается, когда вы хотите получить сравнение на основе символов, но мы можем сделать немного:

const char *to_str(enum fields f)
{
#define FIELD(x) case x: return #x
    switch(f)
      {
        FIELD(signal_path);
        FIELD(trace_length);
        FIELD(sample_rate);
        default: return "<unknown>";
      }
#undef FIELD
}

enum fields from_str(const char *c)
{
#define FIELD(x) if(!strcmp(c, #x)) return x
        FIELD(signal_path);
        FIELD(trace_length);
        FIELD(sample_rate);
        default: return UNKNOWN_fields;
#undef FIELD
}

enum fields from_abv(char *c)
{
    for(enum fields i = BEGIN_fields; i < END_fields; i++)
      {
        char *field = field_str(i);
        if(tolower(c[0]) == field[0] && tolower(c[1]) == strchr(field, '_')[1])
            return i;
      }
    return UNKNOWN_fields;
}

Ваши if заявления могут быть заменены на:

theMeas[i][from_abv(chunk)] = atoi(&chunk[2]);

Или, более безопасно:

enum fields j = from_abv(chunk);
if(j != UNKNOWN_fields) theMeas[i][j] = atoi(&chunk[2]);
else /* erroneous user input */;

Это как можно ближе.

Обратите внимание, что я намеренно использовал схему именования, чтобы упростить создание макросов, которые автоматизируют большую часть этого. Давайте попробуем:

#define member(name, ...) \
  enum name { __VA_ARGS__, \
              M_END_##name, \
              M_UNKNOWN_##name, \
              M_BEGIN_##name = 0 }

#define miter(name, var) \
        enum name var = M_BEGIN_##name; var != M_END_##name; var++

#define msize(name) M_END_##name

Использование:

// define our fields
member(fields, signal_path, trace_length, sample_rate);

// declare object with fields
int theMeas[N][msize(fields)];

for(size_t i = 0; i < N; i++)
    // iterate over fields
    for(miter(fields, j))
        // match against fields
        if(j == from_abv(chunk))
            theMeas[i][j] = atoi(&chunk[2]);

Этот последний бит не кажется таким уж плохим. Он по-прежнему позволяет вам что-то близкое к struct -подобному доступу через theMeas[i][signal_path], но позволяет перебирать «члены» и скрывает большую часть тяжелой работы за макросами.

Функции to_str и from_str требуют немного больше макросов для автоматизации. Вам, вероятно, придется посмотреть на P99 для этого. Функцию from_abv я бы не рекомендовал для общего случая, поскольку у нас нет способа гарантировать, что в следующий раз, когда вы создадите итерируемые поля, вы будете использовать имена с подчеркиванием. (Конечно, вы можете отказаться от функции from_abv и дать своим членам такие непостижимые имена, как SP, TL и SR, что позволит вам напрямую сравнивать их со строковыми данными, но вам придется изменить от strcmp до memcmp с аргументом размера (sizeof(#x) - 1). Тогда все места, которые у вас есть from_abv, вы просто используете from_str, который может быть автоматически сгенерирован для вас.)

Тем не менее, from_abv не сложно определить, и вы можете честно просто скопировать и вставить в него свои блоки if сверху - это будет немного более эффективно, хотя, если вы добавите «член», вы придется обновить функцию (как написано, она обновится сама, если вы добавите участника.)

1 голос
/ 12 мая 2011

C поддерживает указатели на функции , так что вы можете создать массив указателей на функции и обращаться к массиву в соответствии с вашими данными.Это потребует от вас реализации дополнительных функций с такими же сигнатурами.

Другим способом может быть инкапсуляция предложений if в отдельной функции и вызов ее с аргументами.

Однако я думаю, чтотаким образом вы значительно увеличите скорость, если есть.

0 голосов
/ 12 мая 2011

Если (и это довольно много, если), вы можете положиться на данные всегда , являющиеся одним из этих трех вариантов, тогда мы могли бы построить «минимальный идеальный хеш» для трех случаев. Предполагая, что кодировка ASCII (или соответствует ASCII):

L = 76, 0 mod 4
P = 80, 0 mod 4
R = 82, 2 mod 4
S = 83, 3 mod 4
T = 84, 0 mod 4

Итак, S + P - 3 мода 4, T + L - 0 мода 4, а S + R - 1 мода 4. Не минимально, но достаточно близко:

size_t lookup[3] = {
    offsetof(Mea, trace_length),
    offsetof(Mea, sample_rate),
    0,
    offsetof(Mea, signal_path)
};

size_t offset = lookup[((unsigned)chunk[0] + chunk[1]) % 4];

for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) {
    int *fieldptr = (int*)(((char*)(theMeas+i)) + offset);
    *fieldptr = atoi(&chunk[2]);
}

Вы можете предпочесть нанести помаду на эту свинью с помощью макросов или встроенных функций или вместо int *fieldptr иметь char *fieldptr, начать с ((char*)theMeas) + offset и увеличивать ее на sizeof(Mea) каждый раз.

Если вы не можете полагаться на дружественные данные, тогда вам нужна хотя бы одна ветка какого-либо вида (условная или вызов через указатель на функцию), просто чтобы избежать записи чего-либо в случае, если данные плохие. Даже для того, чтобы оставить его равным 1, вам, вероятно, понадобится таблица поиска с 64 тыс. Записей для 3 случаев, что довольно редко, так что вам, вероятно, лучше использовать условные выражения.

0 голосов
/ 12 мая 2011

К сожалению, для простого C99 это невозможно, поскольку индексы массива могут быть только целыми числами без знака.Но, вероятно, функция strncmp() подходит вам больше?

#define EQUALN(a,b,n) (strncmp(a, b, n) == 0)

...

if(EQUALN(chunk, "SP", 2)) {
    for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) {
        theMeas[i].signal_path = atoi(&chunk[2]);
    }
}    
else if(EQUALN(chunk, "TL", 2)) {
    for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) {
        theMeas[i].trace_length = atoi(&chunk[2]);
    }
}    
else if(EQUALN(chunk, "SR", 2)) {
    for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) {
        theMeas[i].sample_rate = atoi(&chunk[2]);
    }
}
0 голосов
/ 12 мая 2011

Вы можете переписать свою логику примерно так, используя указатель на целое число:

while (*ptr != '\0') {
    int items_read = sscanf(ptr, "%31[^;]%n", chunk, &n);

    int *p = NULL;
    if(chunk[0] == 'S' && chunk[1] == 'P') {
        p = &theMeas[i].signal_path;
    }    
    if(chunk[0] == 'T' && chunk[1] == 'L') {
        p = &theMeas[i].trace_length;
    }    
    if(chunk[0] == 'S' && chunk[1] == 'R') {
        p = &theMeas[i].sample_rate;
    }

    for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) {
        *p = atoi(&chunk[2]);
    }   

Этот подход отделяет решение о том, какую переменную изменить (операторы if) от кода, который на самом делевыполняет работу, которая является общей для каждого случая (цикл for).

Вы, конечно, захотите проверить, не соответствует ли p == NULL в случае chunk[0] и chunk[1] что-либо, чем вы былиожидая.

...