🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.lang.misc
Article #3372

Re: Dynamic typing (part 3)

#3372
From: Joe Keane
Date: Wed, 03 Apr 1991 23:24
327 lines
10197 bytes
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 <stdio.h>
#include <sys/time.h>

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 <stdio.h>
#include <sys/time.h>

#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)
Technical Details
Message-ID: <4716@osc.COM>
Path: arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!rpi!usc!elroy.jpl.nasa.gov!decwrl!asylum!osc!jgk
References: <815@optima.cs.arizona.edu> <W+3AE38@xds13.ferranti.com> <20MAR91.08580313@uc780.umd.edu> <JT4AE6F@xds13.ferranti.com> <21MAR91.23594992@uc780.umd.edu> <IA6A5Q4@xds13.ferranti.com> <22MAR91.20485982@uc780.umd.edu> <039AIL3@xds13.ferranti.com>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!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> <W+3AE38@xds13.ferranti.com> <20MAR91.08580313@uc780.umd.edu> <JT4AE6F@xds13.ferranti.com> <21MAR91.23594992@uc780.umd.edu> <IA6A5Q4@xds13.ferranti.com> <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