Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you help me with a symbol-state table and nested switch? Exercise from Illustrating C- Donald Alcock

OK, first thank you for taking the time to read my post!! (^o^)/ And before I put the whole problem a little of context: I am learning by myself 'C' and found the book "Illustrating C" which I´m working. In his book, Donald Alcock used a symbol-state table for the logic in a program that asks to change a Roman numeral in Arabic number.

This is the code:

#include <stdio.h>
char Symbol [] = { 'M', 'D', 'C', 'L', 'X', 'V', 'I' };
long Table [16] [8] =
{
    { 100000, 50001, 10003, 5007, 1006, 512, 111, 0 },
    {      0,     0, 10002, 5007, 1006, 512, 111, 0 },
    {      0,     0, 10004, 5007, 1006, 512, 111, 0 },
    {  80005, 30005, 10004, 5007, 1006, 512, 111, 0 },
    {      0,     0, 10005, 5007, 1006, 512, 111, 0 },
    {      0,     0,     0, 5007, 1006, 512, 111, 0 },
    {      0,     0,  8010, 3010, 1009, 512, 111, 0 },
    {      0,     0,     0,    0, 1008, 512, 111, 0 },
    {      0,     0,     0,    0, 1009, 512, 111, 0 },
    {      0,     0,     0,    0, 1010, 512, 111, 0 },
    {      0,     0,     0,    0,    0, 512, 111, 0 },
    {      0,     0,     0,    0,  815, 315, 114, 0 },
    {      0,     0,     0,    0,    0,   0, 113, 0 },
    {      0,     0,     0,    0,    0,   0, 114, 0 },
    {      0,     0,     0,    0,    0,   0, 115, 0 },
    {      0,     0,     0,    0,    0,   0,   0, 0 }
};

int main ( void )
{
    long Entry = 1, Number = 0;
    int Column, Row = 0;
    char Ch;
    printf ("\nEnter a number\n");
    while ( ((Ch = getchar()) != '\n') && Entry )
    {
        for ( Column=0; Column<7 && (Ch != Symbol[Column]); ++Column );
        Entry = Table [Row][Column];
        Number += Entry / 100;
        Row = Entry % 100;
        printf(" %li ", Number);
    }
    //printf("%c,%c  ", Ch,'\n' && Entry);
    if (Entry)
        printf ("= %ld in Arabics", Number);
    else
        printf ("\nError");
    printf("\nEnd of run");
    return 0;
}

Here a link to pic of the book: roman_2

Well, there he explain the logic of the table.

Later he wrote this about switches and symbol-state tables:

Nested switch statements are useful for implementing the logic contained in symbol-state tables. The outer switch is given a case for each state (row) of the table. The logic in each of these cases comprises an inner switch having a case for each symbol (column) of the table.

Using what he says this code can be replaced

while ( ((Ch = getchar()) != '\n') && Entry )
        {
            for ( Column=0; Column<7 && (Ch != Symbol[Column]); ++Column );
            Entry = Table [Row][Column];
            Number += Entry / 100;
            Row = Entry % 100;
            printf(" %li ", Number);
        }

for this one

while ( ((Ch = getchar()) != '\n') && Entry )
     {
       switch (Row)
        {
          case 0:
                switch (Column)
                {
                   case 'M':
                   case 'D':
                   ...
                }
          ...
         }
     }

I guess you get the idea now.

Well, finally when everything fell apart. ( T.T)

In the exercise 4.2 chapter, say this:

Write a function, using a symbol-state table, to read an octal number from the keyboard, converting it to a decimal integer (of type long) .Allow a preceding + or - sign. For example, the program should read -74 and get the result -60. Your state table should have four columns. These are: [0] to deal with the leading + or - , [1] to deal with any digit from 0 to 7, 2 to deal with a space character, [3] to deal with any other character (an error). The value in each cell should comprise a label (for use in an associated 'switch' statement) and the number of the next 'state', or row. The 'case' associated with valid digits should multiply the accumulating result by the number base, 8, then add the current digit. Write a test-bed program to read octal numbers from the keyboard and display their decimal equivalents on the screen.

and this what I have to this far:

#include <stdio.h>

char Simbol [] = {'0', '1', '2', '3', '4', '5', '6', '7'};
char   Sign [] = {'+', '-'};

long Table [8] [4] =
{
    {  143,  100,   132,  0},
    {  145,  101,     0,  0},
    {    0,  102,     0,  0},
    {    0,  103,     0,  0},
    {    0,  104,     0,  0},
    {    0,  105,     0,  0},
    {    0,  106,     0,  0},
    {    0,  107,     0,  0}
};

int main ( void )
{
    long Entry = 1, Number = 0, Simbo;
    int Column=0, Row;
    int base = 8;
    char Ch;

    printf ("\nEnter a number in base 8 (Octal)\n");
    while ( ((Ch = getchar()) != '\n') && Entry )
    {
        if( (Ch == '+') || ( Ch == '-') )
        {
            for ( Row=0; Row<2 && (Ch != Sign[Row]); ++Row );
        }
        else
        {
           for ( Row=0; Row<8 && (Ch != Simbol[Row]); ++Row );
           Column = 1;
        }

        Entry = Table [Row][Column];
        Simbo = Entry % 100;


        printf("Number %li Simbo %li Column %i \n", Number, Simbo, Column);
        switch (Row)
        {
          case 0:
                switch (Simbo)
                {
                   case 43:{
                           printf("In and is a +\n");
                           Entry = Table [Row][Column];
                           Column = Entry / 100;
                           Number= (+1)*Number;
                           break;}
                   case 0: {
                          Number = ((Number*base) +Simbo);
                          Entry = Table [Row][Column];
                          Column = Entry / 100;
                          break;
                          }
                   case 2: break;
                   case 3: break;
                   default: printf("case 0\n");
                }
                break;

          case 1:
                switch (Simbo)
                {
                   case 45:{
                           printf("In and is a  -\n");
                           Entry = Table [Row][Column];
                           Column = Entry / 100;
                           Number= (-1)*Number;
                           break;
                           }
                   case 1: {
                          Number = ((Number*base) +Simbo);
                          Entry = Table [Row][Column];
                          Column = Entry / 100;
                          break;
                          }
                   case 2: break;
                   case 3: break;
                   default: printf("case 1\n");
                }
                break;

          case 2:
                switch (Simbo)
                {
                   case 0: break;
                   case 2:{
                          Number = ((Number*base) +Simbo);
                          Entry = Table [Row][Column];
                          Column = Entry / 100;
                          break;
                          }
                   default: printf("case 2\n");
                }
                break;

          case 3:
                switch (Simbo)
                {
                   case 0: break;
                   case 3:{
                          Number = ((Number*base) +Simbo);
                          Entry = Table [Row][Column];
                          Column = Entry / 100;
                          break;
                          }
                   default: printf("case 3\n");
                }
                break;

          case 4:
                switch (Simbo)
                {
                   case 0: break;
                   case 4:{
                          Number = ((Number*base) +Simbo);
                          Entry = Table [Row][Column];
                          Column = Entry / 100;
                          break;
                          }

                   default: printf("case 4\n");
                }
                break;

          case 5:
                switch (Simbo)
                {
                   case 0: break;
                   case 5:{
                          Number = ((Number*base) +Simbo);
                          Entry = Table [Row][Column];
                          Column = Entry / 100;
                          break;
                          }

                   default: printf("case 5\n");
                }
                break;

          case 6:
                switch (Simbo)
                {
                   case 0: break;
                   case 6:{
                          Number = ((Number*base) +Simbo);
                          Entry = Table [Row][Column];
                          Column = Entry / 100;
                          break;
                          }
                   default: printf("case 6\n");
                }
                break;

          case 7:
                switch (Simbo)
                {
                   case 0:  break;
                   case 7:{
                          Number = ((Number*base) + Simbo);
                          Entry = Table [Row][Column];
                          Column = Entry / 100;
                          break;
                          }
                   default: printf("case 7\n");
                }
                break;

        default: printf("\ndefault\n");
        }

        printf("\n---Number %li\n\n", Number);

    }
     if (Entry)
         printf ("\n\n= %ld Decimal", Number);
     else
        printf ("\nError");
     return 0;
}

Works fine with some little problems but converts the octal to decimal.

The problem is I can´t understand how to use the table with the switch and delete the if´s and for's here:

while ( ((Ch = getchar()) != '\n') && Entry )
    {
        if( (Ch == '+') || ( Ch == '-') )
        {
            for ( Row=0; Row<2 && (Ch != Sign[Row]); ++Row );
        }
        else
        {
           for ( Row=0; Row<8 && (Ch != Simbol[Row]); ++Row );
           Column = 1;
        }

        Entry = Table [Row][Column];
        Simbo = Entry % 100;
    ...
   }

And the space character...I just don't know what to do with it!!!!.

In few words:

How to use the table with switchs for avoid if´s and for´s when the table have symbols in different columns and row (unlike roman numbers, each symbol with a column...)with each cell having a label and a next state.

Sorry for make this too long and for any error you found in the grammar, English is no my native language.

Thanks again!

like image 285
Incubus_inside Avatar asked Jul 25 '11 02:07

Incubus_inside


1 Answers

In my opinion the key to understanding the use of the table is in these two lines in the inner loop.

        Number += Entry / 100;
        Row = Entry % 100;

The values stored in the table have 2 "fields" in a packed decimal representation. There's a little loop to search for the input character in a char array (strchr would be a more common ways to do it). Then the value from the table is decomposed using / and % to get the "tens" place and the "ones" place which indicate the next state (the Row value for the next iteration of the big loop), and "hundreds" place and higher places which, when divided by 100, give the amount that this character contributes to the output value.

Alright so first you need to do something like that little loop in the example. This is the character classification function. It lets you take all those character constants out of the code and put them in a data structure where they belong.

That said, I'm off to try my hand at writing such a program myself. Not sure if you're looking for a full solution or just a little help. I'll check back when I've got more.

Edit: I think the problem statement is a little misleading. You'll need a column for each valid character. Take a look at this version of the tables.

/* the state transition table: +-, 0-7, ' ', ERR */
              /* 01234567890 */
char symbol[] = "+-01234567 ";
int table[][] = {
        /*  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,10,11 */
/* 0*/  {   2,   1, 002, 102, 202, 302, 402, 502, 602, 702, 0, 0 },
/* 1*/  {   0,   0,-001,-101,-201,-301,-401,-501,-601,-701, 0, 0 },
/* 2*/  {   0,   0, 002, 102, 202, 302, 402, 502, 602, 702, 0, 0 },
};

I strongly recommend putting comments like these around your tables, they help you find your way around. By the way, I can't figure out what the space is supposed to do either.

Edit: Using a macro helps make the fields easier to see.

#define T(x,y) ((x*100)+y)
/* the state transition table: +-, 0-7, ' ', ERR */
              /* 01234567890 */
char symbol[] = "+-01234567 ";
int table[][] = {
{ T(0,2), T(0,1), T(0,02), T(1,02), T(2,02), T(3,02), T(4,02), T(5,02), T(6,02), T(7,02), T(0,0), 
    T(0,0) },
{ T(0,0), T(0,0), T(0,01),T(-1,01),T(-2,01),T(-3,01),T(-4,01),T(-5,01),T(-6,01),T(-7,01), T(0,0), 
    T(0,0) },
{ T(0,0), T(0,0), T(0,02), T(1,02), T(2,02), T(3,02), T(4,02), T(5,02), T(6,02), T(7,02), T(0,0), 
    T(0,0) },
};

Later ... I have recently used the same data structure and algorithm for a scanner for an APL-style programming language (posted for review).

like image 160
luser droog Avatar answered Sep 18 '22 14:09

luser droog