Path: utzoo!utgpu!news-server.csri.toronto.edu!rpi!usc!elroy.jpl.nasa.gov!decwrl!asylum!osc!jgk From: jgk@osc.COM (Joe Keane) Newsgroups: comp.lang.misc Subject: Re: Dynamic typing (part 3) Summary: Declare variables in blocks. Keywords: scope Message-ID: <4716@osc.COM> Date: 3 Apr 91 23:24:59 GMT References: <815@optima.cs.arizona.edu> <20MAR91.08580313@uc780.umd.edu> <21MAR91.23594992@uc780.umd.edu> <22MAR91.20485982@uc780.umd.edu> <039AIL3@xds13.ferranti.com> Reply-To: jgk@osc.COM (Joe Keane) Organization: Versant Object Technology, Menlo Park, CA Lines: 327 In article <22MAR91.20485982@uc780.umd.edu> cs450a03@uc780.umd.edu writes: >Generally, each assignment to a variable is unique. (I try not to >re-assign, and when I do, I try and make sure re-executing that >section of code would not cause a problem). Exception made for loop >counters, but not for other assignments made within the loop. In article <039AIL3@xds13.ferranti.com> peter@ficc.ferranti.com (Peter da Silva) writes: >This is an unusual coding style, in my experience. Are you actually >limitin assignments, or are you hiding those assignments in call by >reference? Perhaps a code fragment would help. I don't think this is so unusual. I do the same thing to a large extent when programming in C or C++. Perhaps it could be my exposure to functional languages, but i find that this approach goes well with C too. I'm not strict about it, but i find that almost all variables can be done this way. We have to make a few exceptions, which i'll describe later. There's another thing i do which goes together with this. I generally try to keep variables' scope as small as possible. For example, if a variable is used inside a loop but it's not carried between iterations, then i can declare it inside the loop body. Given the `small as possible' rule, that means i should declare it there. Sometimes i make blocks just to declare variables in. This may sound crazy, but it makes sense because these blocks generally correspond to some conceptual operation. I think the result is that code written this way is easier to read and understand what's going on. By making the lifetime of a variable clear, you help the human reader understand the general structure of the algorithm. Optimizing compilers can figure it out too, but people don't want to spend time doing this. Loops are an exception to this rule. Obviously, you have to increment or somehow advance your loop variable in the loop, or else it gets stuck. And often the point of a loop is to accumlate some value. You asked for an example. I don't think a short example would illustrate the point well. So free with this posting, i'm going to give you a real program. This is actually a useful program, so you may want to save the good version. First i show the `bad' version of the program. It suffers from what i call `Pascal variable declaration syndrome'. All the variables are declared at the top of the function, far away from where they're actually used. You can't see where variables are used or how long they're expected to live. Variables are re-used without this being clear. You may say that the problem is that my main function is too big. I don't agree with this, although i suppose this is a matter of taste. I believe that chopping it up into little functions would not improve readability. With proper attention to scoping, which is what i'm talking about after all, each block is like a small function. But the blocks are ordered exactly like they're used, so you don't have to go jumping all around the source to see something simple. -- don't cut here -- don't cut here -- don't cut here -- don't cut here -- #include #include extern char* malloc(); extern char* realloc(); struct line { int length; char* ptr; }; int main (argc, argv) int argc; char* argv[]; { struct line* master_ptr; int master_size; int master_capacity; char *buffer_ptr; int buffer_capacity; int c; int buffer_size; char* line_ptr; int pass; struct timeval tv; int line_number; int other_line; struct line temp; char* ptr; char* end; master_capacity = 256; master_ptr = (struct line*)malloc(master_capacity * sizeof(struct line)); if (!master_ptr) goto out_of_memory; master_size = 0; buffer_capacity = 256; buffer_ptr = malloc(buffer_capacity); if (!buffer_ptr) goto out_of_memory; for (;;) { c = getchar(); if (c == EOF) goto eof; if (master_size >= master_capacity) { master_capacity *= 2; master_ptr = (struct line*)realloc(master_ptr, master_capacity * sizeof (struct line)); if (!master_ptr) goto out_of_memory; } buffer_size = 0; while (c != '\n') { if (buffer_size >= buffer_capacity) { buffer_capacity *= 2; buffer_ptr = realloc(buffer_ptr, buffer_capacity); } buffer_ptr[buffer_size] = c; buffer_size++; c = getchar(); if (c == EOF) { fputs("shuffle: adding newline at end of file\n", stderr); break; } } line_ptr = malloc(buffer_size); if (!line_ptr) goto out_of_memory; memcpy(line, buffer_ptr, buffer_size); master_ptr[master_size].length = buffer_size; master_ptr[master_size].ptr = line_ptr; master_size++; if (c == EOF) goto eof; } eof: free(buffer_ptr); for (pass = 0; pass < 16; pass++) { gettimeofday(&tv, 0); srandom(tv.tv_sec ^ tv.tv_usec); for (line_number = 0; line_number < master_size; line_number++) { other_line = random() % (master_size - line_number) + line_number; temp = master_ptr[line]; master_ptr[line_number] = master_ptr[other_line]; master_ptr[other_line] = temp; } } for (line_number = 0; line_number < master_size; line_number++) { ptr = master_ptr[line_number].ptr; end = ptr + master_ptr[line_number].length; while (ptr < end) { putchar(*ptr); ptr++; } putchar('\n'); } return 0; out_of_memory: fputs("shuffle: out of memory\n", stderr); return 1; } -- don't cut here -- don't cut here -- don't cut here -- don't cut here -- Now i'm going to show you how i actually wrote it. None of the variables are changed, but they're declared in the right place. I've also added a couple blocks, like i said before. I've also restored some debugging code, because you may find it useful. It also satisfies the single-assignment property much better than the `bad' version, because variables are created when they're needed rather than at the beginning of the function. -- cut here -- cut here -- cut here -- cut here -- cut here -- cut here -- #include #include #define DEBUG 0 extern char* malloc(); extern char* realloc(); struct line { int length; char* ptr; }; int main (argc, argv) int argc; char* argv[]; { struct line* master_ptr; int master_size; #if DEBUG fputs("shuffle: reading lines...\n", stderr); #endif { int master_capacity; char *buffer_ptr; int buffer_capacity; master_capacity = 256; master_ptr = (struct line*)malloc(master_capacity * sizeof(struct line)); if (!master_ptr) goto out_of_memory; master_size = 0; buffer_capacity = 256; buffer_ptr = malloc(buffer_capacity); if (!buffer_ptr) goto out_of_memory; for (;;) { int c; int buffer_size; char* line_ptr; c = getchar(); if (c == EOF) goto eof; if (master_size >= master_capacity) { master_capacity *= 2; master_ptr = (struct line*)realloc(master_ptr, master_capacity * sizeof (struct line)); if (!master_ptr) goto out_of_memory; } buffer_size = 0; while (c != '\n') { if (buffer_size >= buffer_capacity) { buffer_capacity *= 2; buffer_ptr = realloc(buffer_ptr, buffer_capacity); } buffer_ptr[buffer_size] = c; buffer_size++; c = getchar(); if (c == EOF) { fputs("shuffle: adding newline at end of file\n", stderr); break; } } line_ptr = malloc(buffer_size); if (!line_ptr) goto out_of_memory; memcpy(line_ptr, buffer_ptr, buffer_size); master_ptr[master_size].length = buffer_size; master_ptr[master_size].ptr = line_ptr; master_size++; if (c == EOF) goto eof; } eof: free(buffer_ptr); } #if DEBUG fprintf(stderr, "shuffle: total of %d lines read\n", master_size); #endif { int pass; for (pass = 0; pass < 16; pass++) { struct timeval tv; int line_number; gettimeofday(&tv, 0); #if DEBUG fprintf(stderr, "shuffle: doing pass %d, time is %d seconds, %d microseconds...\n", pass, tv.tv_sec, tv.tv_usec); #endif srandom(tv.tv_sec ^ tv.tv_usec); for (line_number = 0; line_number < master_size; line_number++) { int other_line; struct line temp; other_line = random() % (master_size - line_number) + line_number; temp = master_ptr[line_number]; master_ptr[line_number] = master_ptr[other_line]; master_ptr[other_line] = temp; } } } #if DEBUG fputs("shuffle: writing lines...\n", stderr); #endif { int line_number; for (line_number = 0; line_number < master_size; line_number++) { char* ptr; char* end; ptr = master_ptr[line_number].ptr; end = ptr + master_ptr[line_number].length; while (ptr < end) { putchar(*ptr); ptr++; } putchar('\n'); } } #if DEBUG fputs("shuffle: all done\n", stderr); #endif return 0; out_of_memory: fputs("shuffle: out of memory\n", stderr); return 1; } -- cut here -- cut here -- cut here -- cut here -- cut here -- cut here -- -- Joe Keane, C++ hacker jgk@osc.com (...!uunet!stratus!osc!jgk)