Need help understanding the C function

Here's the deal: I'm trying, as a learning experience, to convert the program to C ++. This program takes a text file and makes changes to it according to the rules entered by the user. Specifically, it applies sound changes to a set of words using rules formatted as "s1 / s2 / env". s1 represents the characters to change, s2 represents what to change, and env is the context in which the change should be applied.

I'm sorry that I don't describe this in more detail, but the question will be too long and the author's site already explains it.

I am having trouble with TryRule. I understand that he needs to see if a given rule applies to a given string, but I am having a hard time understanding how this is done. The poor explanation of the parameters confuses me: for example, I don't understand why the strings "s1" and "s2" have to be passed back or what "i" means.

This is the code:

/*
**  TryRule
**
**  See if a rule s1->s2/env applies at position i in the given word.
**
**  If it does, we pass back the index where s1 was found in the
**  word, as well as s1 and s2, and return TRUE.
**
**  Otherwise, we return FALSE, and pass garbage in the output variables.
*/
int TryRule( char *word, int i, char *Rule, int *n, char **s1, char **s2, char *varRep )
    {
        int j, m, cont = 0;
        int catLoc;
        char *env;
        int  optional = FALSE;
        *varRep = '\0';

        if (!Divide( Rule, s1, s2, &env ) || !strchr( env, '_' ))
            return(FALSE);

        for (j = 0, cont = TRUE; cont && j < strlen(env); j++)
        {
            switch( env[j] )
            {
                case '(':
                    optional = TRUE;
                    break;

                case ')':
                    optional = FALSE;
                    break;

                case '#':
                    cont = j ? (i == strlen(word)) : (i == 0); 
                    break;

                case '_':
                    cont = !strncmp( &word[i], *s1, strlen(*s1) );
                    if (cont)
                    {
                        *n = i;
                        i += strlen(*s1);
                    }
                    else
                    {
                        cont = TryCat( *s1, &word[i], &m, &catLoc );
                        if (cont && m)
                        {
                            int c;
                            *n = i;
                            i += m;

                            for (c = 0; c < nCat; c++)
                                if ((*s2)[0] == Cat[c][0] && catLoc < strlen(Cat[c]))
                                    *varRep = Cat[c][catLoc];
                        }
                        else if (cont)
                            cont = FALSE;
                    }
                    break;

                default:
                    cont = TryCat( &env[j], &word[i], &m, &catLoc );
                    if (cont && !m)
                    {
                        /* no category applied */
                        cont = i < strlen(word) && word[i] == env[j];
                        m = 1;
                    }
                    if (cont)
                        i += m;
                    if (!cont && optional)
                        cont = TRUE;
            }
        }
        if (cont && printRules)
            printf( "   %s->%s /%s applies to %s at %i\n", 
            *s1, *s2, env, word, *n );

    return(cont);
}

      

+1


a source to share


3 answers


This code is ... hard to read. I looked at the original file and it may indeed use a few better variable names. I especially like this part from one of the function's comments:

/*
** (Stuff I removed)
**
** Warning: For now, we don't have a way to handle digraphs. 
**
** We also return TRUE if (<- It really just stops here!)
*/

      

I see a challenge. I agree with wmeyer about variables. I think I understand things, so I will try to translate the function into pseudocode.

Word: the string we are looking at i: the index in the string we are looking at
Rule: the text of the rule (ie "V / b / _")
n: variable to return the index to the string we found a match for _ I think
s1: returns the first part of the rule decoded from the rule
s2: returns the second part of the rule decoded from the rule
varRep: returns the character matched in the category if the category matches, I think



int TryRule( char *word, int i, char *Rule,
                int *n, char **s1, char **s2, char *varRep ) {
        Prepare a bunch of variables we''ll use later
        Mark that we''re not working on an optional term
        Set varRep' first char to null, so it' an empty string

        if (We can parse the rule into it' parts
              OR there is no _ in the environment (which is required))
            return FALSE // Error, we can't run, the rule is screwy

        for (each character, j, in env (the third part of the rule)) {
            if (cont is TRUE) {
                switch (the character we''re looking at, j) {
                    if the character is opening paren:
                        set optional to TRUE, marking it' an optional character
                    if the character is closing paren:
                        set optional to FALSE, since we''re done with optional stuff
                    if the character is a hash mark (#):
                        // This is rather complicated looking, but it not bad
                        // This uses a ? b : c, which means IF a THEN b ELSE c
                        // Remember i is the position in the word we are looking at
                        // Hash marks match the start or end of a word
                        // J is the character in the word

                        if (j >= 0) {
                            // We're not working on the first character in the rule
                            // so the # mark we found is to find the end of a word

                            if (i == the length of the word we''re looking at) {
                                // We've found the end of the word, so the rule matches

                                continue = true;   // Keep going
                            } else {
                                // We're not at the end of a word, but we found a hash
                                // Rule doesn't match, so break out of the main loop by setting
                                //     continue to false

                                continue = false;
                            }
                        } else {
                            // OK, the hash mark is the first part of env,
                            // so it signifies the start of a word

                            continue = (i == 0);   // Continue holds if we
                                                   // are matching the first
                                                   // character in *word or not
                        }
                    if the character is an _ (the match character):
                        // This gets complicated

                        continue = if word starting at character i ISN''T s1, the search string;

                        if (continue == TRUE) {
                            // There was no match, so we'll go look at the next word
                            n = the index of the word start that didn''t match   // Not sure why
                            i = i (start index to look) + length of s1 (word we just matched)
                            // This means i now holds the index of the start of the next word
                        } else {
                            // TryCat sees if the character we're trying to match is a category

                            continue = s1 is a category in the program
                                          && the category contains the character at word[i]

                            // If continue holds false, s1 was a category and we found no match
                            // If continue holds true, s1 either wasn't a category (so m = 0)
                            //     or s1 WAS a category, m contains 1, and catLoc holds which
                            //     character in the category definition was matched

                            if (we found a match of some sort
                                   && s1 was a category (indicated by m == 1)) {
                                n = index of the character in the word we found a match
                                i = the index of the next character (m is always 1, so this is ugly)

                                for (each category defined) {
                                    if (first character of s2
                                           == the category' name
                                        && where in the category definition we matched
                                              is less than the length of the category we''re on) {
                                           varRep = the character matched in the category
                                        }
                                }

                                // Now the above seems EXACTLY like the TryCat function. You'd
                                // think varRep would always hold the same value as catLoc. I
                                // believe this loop is so that later rules also get applied?
                            } else {
                                continue = FALSE; // Because we didn't match a letter or category
                            }
                        }
                    Any other character:
                        continue = the character we''re looking at is a category in the program
                                      && the category contains the character at word[i]

                        if (there was a match AND it wasn''t a category (m == 0, just a letter)) {
                            m = 1;
                            continue if and only if there are characters left in the word
                                 (i < strlen()) && the current character is at word[i]
                                 (we matched a literal character, instead of a category)
                        }

                        if (continue)
                            i = i + m // Remember, M is always 1 or 0
                                      // So this is basically IF continue THEN i++ END IF
                        if ((continue == FALSE) && (optional == TRUE))
                            // We didn't find a match, but we're working on an optional part
                            // So continue anyway
                            continue = TRUE;
                end switch
             end if continue == true
        }
    }

    if (continue && printRules)
        print out a little debug statement showing what we matched

    return continue;   // At this point, if continue is false we can't keep matching
}

      

Hope this helps. You may need to read it several times. It took me over 45 minutes to write this, almost entirely due to trying to decipher what is happening in some cases around TryCat. Add after about 5 minutes to constantly try to hit the Tab key and get my cursor to submit to the next field (silly HTML textbox).

Sorry this is so big you probably have to do a bunch of horizontal scrolling.

+2


a source


Given that you are converting from C to C ++, you need to refactor your code to make it more readable.

One of the main problems with this code is that the variables have terrible names, and I would parry even the original script of this program to spend some time analyzing it.



Simply renaming the variables to be more precise will give you a big hand in understanding what the code is doing.

Have a look at some of the questions flagged in the refactoring for some help. There is also Refactoring by Martin Fowler

0


a source


I think you need all the code to understand this snippet.

It looks like "word", "i" and "rule" are input variables, the rest are pure output variables.

"i" is the current index in the "word", i.e. TryRule only looks at "word" starting with "word [i]".

In "s1", the functions return the left side of the applied rule. The "s2" is the right side of this rule.

In "n", the function returns the position in "word" where the rule applies.

Don't know what "varRep" is.

0


a source







All Articles