Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic Input for Statistics

Tags:

c

bash

Is there an equivalent solution similar to this implementation I've written in bash? Normally, I've always handled dynamic allocation like so:

(I like the second implementation because it's flexible and I don't need to know exactly how many inputs I need, I can input them as is. How can I accomplish a similar approach in C?

C Implementation:

double* get_data(int* data_size)
{
    double* data_set = NULL;
    int size = get_size();
    int i;

    *data_size = size;

    data_set = malloc(size * sizeof(double));

    for(i = 0; i < size; i++) 
    {
        printf("Enter statistical data: ");
        scanf("%lf", &data_set[i]);
    }

    return data_set;
}

Bash Implementation:

data_set=()
while IFS= read -r -p 'Enter statistical data (empty line to quit):' input; do
  [[ $input ]] || break
  data_set+=("$input")
done
like image 792
theGrayFox Avatar asked Feb 15 '23 00:02

theGrayFox


2 Answers

The simplest solution is to use C++. But that's not what you're asking, so I'll leave it there.

The following, although it looks awful at first glance, is actually usually pretty efficient (depending on your C library's implementation of realloc, but it's a common idiom in GNU code so the realloc implementation is usually well-adapted to it):

double* get_data(size_t *size_p) {
  size_t n = 0;
  double* data = NULL;
  double val;
  while (get_a_datum(&val)) {
    double* newdata = realloc(data, (n + 1) * sizeof *data);
    if (newdata == NULL) { free(data); report(error); }
    data = newdata;
    data[n++] = val;
  }
  if (size_p) *size_p = n;
  return data;
}

If you're not happy with that approach, you can roll your own exponential realloc, where you track the size of the allocated vector and, if it is about to be exceeded, double it. That's more code, though, and it's highly likely that realloc will do precisely that for you.

like image 149
rici Avatar answered Feb 24 '23 14:02

rici


The trouble with scanf("%lf", &data_set[i]); is that scanf() skips leading white space, including blank lines, silently.

Since you want to terminate on an empty line, the obvious solution seems to be to use fgets() or getline() to read a line and then use sscanf() to read the data when the line is not empty.

Hence:

char line[4096];
while ((fgets(line, sizeof(line), stdin) != 0)
{
    if (line[0] == '\n')
        break;
    if (sscanf(line, "%lf", &data_set[i++]) != 1)
        ...format error...
}

Note that each I/O function is checked. If you enter a blank or two on an otherwise empty line, they'll go into the 'format error' code. You can make the test for 'blank line' more sensitive if you wish (consider using strspn() and strlen(), for example).

like image 27
Jonathan Leffler Avatar answered Feb 24 '23 13:02

Jonathan Leffler