Эффективный способ установить группу переменных на основе другой переменной? - PullRequest
0 голосов
/ 11 мая 2018

Я бы хотел установить группу из 2-4 переменных, значения которых зависят от 5-й переменной. Я могу сделать это с помощью длинного списка if else, мне было интересно, если бы был трюк, о котором я не знаю, я мог бы научиться делать это немного более элегантно? Я все еще очень плохо знаком с `` C.

Например:

/*some code... x is read from stdin...*/

if (x == 1) {
  a = 10;
  b = 100;
}

else if (x == 2) {
  a = 10;
  b = 50;
  c = 100;
}

else if (x == 3) {
  a = 0;
  b = 25;
  c = 50;
  d = 100;
}
/* and so on...*/

Я планирую сделать все это в функции, отличной от main(), используя указатели для установки a, b, c ... но я здесь опущен для простоты.

Приветствия

W

Ответы [ 9 ]

0 голосов
/ 11 мая 2018

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

Приведенный ниже код работает в интерактивном режиме для целей тестирования, отбрасывая все, что вы считаете ненужным.

#include<stdio.h>                                                                     

// Lets get handlers for each value                              
int f_One(void);
int f_Two(void);
int f_Three(void);
int f_Four(void);

// Lets have them in an enum rather than mere numbers, may not be required for you
typedef enum {

    ONE = 1,
    TWO,
    THREE,
    FOUR,

    MAX_TASKS,
    TASK_INVALID = -1,

} eValue_t ;

// This is a structure that binds an input and a handler together
typedef struct stTask {

    eValue_t TaskID;
    int (*handler) (void);

} stTask_t;

// Lets have an object
stTask_t Task;

// This is a like look-up table for referencing inputs and their handlers
const stTask_t TaskList[MAX_TASKS-1] =
{
    { ONE,    f_One   },
    { TWO,    f_Two   },
    { THREE,  f_Three },
    { FOUR,   f_Four  },
}; 

// This is your data
int a, b, c, d;

// Handler for 1
int f_One(void)
{
    printf("\nOne: ");
    a = 10;
    b = 100;
    printf("a is %d, b is %d", a, b);

    return 1;
}

// Handler for 2
int f_Two(void)
{
    printf("\nTwo: ");
    a = 10;
    b = 50;
    c = 100;
    printf("a is %d, b is %d, c is %d", a, b, c);

    return 2;
}

// Handler for 3
int f_Three(void)
{
    printf("\nThree: ");
    a = 0;
    b = 25;
    c = 50;
    d = 100;
    printf("a is %d, b is %d, c is %d, d is %d", a, b, c, d);

    return 3;
}

// Handler for 4
int f_Four(void)
{
    printf("\nFour: I didn't set any :D");
    return 4;
}

int main (void)
{
    int Val = -1;
    int ret = 0;

    while(1)
    {
        // Get input
        printf("\n\nPress 1, 2, 3, 4 or 0 for exit, Enter a value: ");
        scanf("%d", &Val);
        // Some validation, can be improved, or discarded
        if(Val > 4)
        {      
            printf("\nInvalid input");
            continue;
        }
        else if(Val == 0)
        {
            printf("\nExiting...");
            break;
        }
        // Assign the task
        Task.TaskID = Val-1;
        // Load the handler
        Task.handler = TaskList[Task.TaskID].handler;
        // Execute the handler
        ret = Task.handler();
        // Do something with 'ret'
    }
    printf("\n\n");
    return 0;
}
0 голосов
/ 11 мая 2018

Йо может использовать union с перечислением. Размер структуры всегда будет одним из самых больших по структуре контента.

enum type
{
    BAR = 0,
    BAZ,
    OTHER,
};

struct bar
{
    int x;
    char y;
};

struct baz
{
    void * x;
    float y;
};

struct other
{
    char x[2];
};

typedef struct _foo
{
    enum type type;
    union
    {
        struct bar bar;
        struct baz baz;
        struct other other;
    };
} foo;

...

struct bar a = { 1, 'c', };

foo x;
x.type = BAR;
x.bar = a;


struct baz b = { NULL, 1.0f, };

foo y;
y.type = BAZ;
y.baz = b;

if (x.type == BAR)
    printf("%d\n", x.bar.x);

if (y.type == BAZ)
    printf("%p\n", y.baz.x);
0 голосов
/ 11 мая 2018

Если значения x на самом деле последовательны, то вы можете создать массив структур для хранения значений для a, b, c, d.Единственная хитрость в том, что вам нужно значение часового, чтобы указать, что переменная не должна быть установлена.В приведенном ниже примере значение -1 является значением часового.

struct setting
{
    int a, b, c, d;
}
settings[] =
{
    {  0,   0,  0,    0 },     // x = 0 not used
    { 10, 100, -1,   -1 },     // -1 indicates that a variable should be skipped
    { 10,  50, 100,  -1 },
    {  0,  25,  50, 100 },
};
static int maxSetting = sizeof(settings) / sizeof(settings[0]);

void updateSettings(int x, int *a, int *b, int *c, int *d)
{
    if (x > 0 && x < maxSetting)
    {
        struct setting *sptr = &settings[x];
        if (sptr->a >= 0)
            *a = sptr->a;
        if (sptr->b >= 0)
            *b = sptr->b;
        if (sptr->c >= 0)
            *c = sptr->c;
        if (sptr->d >= 0)
            *d = sptr->d;
    }
}
0 голосов
/ 11 мая 2018
static const int a[4] = {10, 10, 0};
static const int b[4] = {100, 50, 25};
static const int c[4] = {0, 100, 50};
static const int d[4] = {0, 0, 100};

printf("%d %d %d %d\n", a[x - 1], b[x - 1], c[x - 1], d[x - 1]);

или с использованием математики и с учетом конкретных значений:

// if (x == 1){a = 10; b = 100;}
// else if (x == 2){a = 10; b = 50; c = 100;}
// else if (x == 3){a = 0; b = 25; c = 50; d = 100;}

void get_values(int x, int *a, int *b, int *c, int *d)
{
   *a = (1 - x / 3) * 10;
   *b = 100 >> (x - 1);
   *c = (x - 1) * (200 >> (x - 1));
   *d = (x / 3) * 10;
}

int main(void)
{
    int a, b, c, d;
    int x = 3;

    get_values(x, &a, &b, &c, &d);
    printf("%d %d %d %d\n", a, b, c, d);
    return (0);
}

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

0 голосов
/ 11 мая 2018

По-моему, за то, что стоит того, что отделяет данные от функциональности программы:

int data[3][4] = {  
   {10, 100, 0, 0},
   {10, 50, 100, 0},
   {0, 25, 50, 100}
};

a, b, c и d становятся отдельной строкойэтот массив, который вы индексируете с помощью

data[-1 + x]
0 голосов
/ 11 мая 2018

Если вы чувствуете, что хотите взломать, вы можете сделать этот причудливый трюк:

typedef enum
{
    CASE_1_ = 1,
    CASE_2_,
    CASE_3_

}states;

void caseA(int *a, int *b, int *c, int *d) {
    *a = 10;
    *b = 100;
}

typedef void (*fp)(int *, int *, int *, int *);

fp proccess[] = {[CASE_1_] = caseA, [CASE_3_] = caseB, [CASE_3_] = caseC};

Все, что вам нужно сделать, это proccess[x]();, и вам не понадобятся операторы switch или if-else.

0 голосов
/ 11 мая 2018

Просто для удовольствия вы можете использовать различные функции. Вот как это можно сделать (на примере использования вы можете легко поместить его в свою if лестницу):

#include <stdarg.h>
#include <stdio.h>

void set_bunch(int n_vars, ...) {
    va_list args;
    va_start(args, n_vars);
    for (int i = 0; i < n_vars; ++i) {
        int* varp = va_arg(args, int*);
        int varv = va_arg(args, int);
        *varp = varv;

    }
    va_end(args);
}

int main() {
    int a, b, c, d;

    set_bunch(2, &a, 20, &b, 30);
    printf("%d %d\n", a, b); // prints 20 30

    set_bunch(3, &a, 30, &b, 40, &c, 50);
    printf("%d %d %d\n", a, b, c); // prints 30 40 50
}
0 голосов
/ 11 мая 2018

Хорошо, оператор switch обычно более элегантен, чем набор if-else операторов

switch (x) {
    case 1:    a = 10;
               b = 100;
               break;

    case 2:    a = 10;
               b = 50;
               c = 100;
               break;

    case 3:    a = 0;
               b = 25;
               c = 50;
               d = 100;
               break;

    default:   // have a default case or catch it as an error
}

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

switch (x) {
    case 1:    caseA(&a, &b);
               break;

    case 2:    caseB(&a, &b, &c);
               break;

    case 3:    caseC(&a, &b, &c, &d);
               break;

    default:   // have a default case or catch it as an error
}

void caseA(int *a, int *b) {
    *a = 10;
    *b = 100;
}
0 голосов
/ 11 мая 2018

Вы можете использовать switch(x), что позволит вам повторно использовать некоторый код, если несколько значений имеют идентичные эффекты.Например,

switch(x){
    case 5:
    case 6:
        a=0;
        b=25; //this code will run for both cases
}

с ограничениями по курсу.Вы не можете применить это к каждому случаю.

Также, используя троичные операторы, вы можете обрабатывать некоторые пограничные случаи.Представьте себе, например, a=10 каждый раз, кроме случаев, когда x==2, тогда это 5.Поэтому вы можете обрабатывать эту единственную переменную следующим образом: a = (x==2)?5:10;

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...