On the Postdiluvian blog, I gave an explanation of how Nanyan Numerals work.  Now I want to show you how to create a simple Python program for converting to and from Nanyan Numerals.  For this post, I’ll focus on converting Nanyan numerals to a more readable integer.

The language of choice for this project is Python.  Open a new file and let’s begin.

We’ll start by adding the Nanyan Numeral we want to convert, and let’s choose the number 5.  To make text entry simple, we’ll use a hyphen (-) in place of θ, since it is more easily found on most keyboards.

CONVERT = "O-|"

Now we’ll add a lookup for the characters.  Each character will code for the numbers and symbols it is supposed to add to the stack.

codes = {
    "|": "1+*",
    "-": "2*",
    "O": "2"
}

Now we’ll add a lookup for the operators.  This will code each operator to a lambda function that will take two parameters.  This will help keep the code clean so there isn’t an ugly “if” statement.

ops = {
    "*": lambda x, y: x * y,
    "+": lambda x, y: x + y
}

Now let’s decode the sequence that we will use to calculate the number.  To do this, we’ll loop through the numeral we want to convert, and we’ll use the “codes” lookup we created above.

n = ""
for c in CONVERT:
    n += codes.get(c, "")

Now to do the calculation, we’ll need a stack.  To do this, I’ll just use a regular list.  At the end, if there is one item left in the stack, that will be the final result.

s = []

Now let’s start looping through “n” and add each character to the stack.

for c in n:
    s.append(c)

Now we need to check if we need to do an operation.  To do that, we’ll check the last item in the stack to see if it is an operator.  (In this context, we’ll check to see if the character is in our ops lookup).  Put this “if” statement inside the for loop we created above.

    if s[-1] in ops:

Inside that if statement, we’ll check to see if there are enough characters on the stack to do the operation.  This is easy because all the operations take two operands, so we’ll check if there are three characters on the stack.  (If there aren’t, we’ll just ignore the operation because that’s how Nanyan Numerals work.)  Put this “if” statement inside the previous “if” statement.

        if len(s) >= 3:

Inside that second if statement we need to grab the last three items off the stack and then remove them.  We could “pop” these off the stack one by one, but the best way is just to grab them all at once and then reassign “s” to a sublist with the final elements removed.  We can do that by employing “unpacking” in our assignment statement.  The “s[-3:]” will be a list of the final three elements of the list.

            o2, o1, o = s[-3:]

To get rid of these elements, we’ll reassign the stack to a sublist of “s” with the final three elements removed (which is what the “[:-3]” is for).

            s = s[:-3]

Now we need to get the function for the operator.  We already know the last item on the stack was an operator in “ops” and we’ve already assigned it to “o” so all we need to do is get the function from the lookup.

            op = ops[o]

Then we need to call the function and get the result.  The operands “o1” and “o2” might be strings (if they came from the initial conversion), so we’ll need to convert each of them to integers using the “int” function.

            r = op(int(o1), int(o2))

And then we need to push the result back on the stack

            s.append(r)

Now we need to add an “else” to that “if” to handle the case where there were not enough operands on the stack to perform an operation.  In that case, we still need to pop off the operator.

        else:
            s.pop()

And finally, we need to print out the answer.  If something went wrong (like the Nanyan numeral not being well-formed), we’ll print out “unknown” Otherwise, we’ll print out the number from the stack.

print s[0] if len(s) == 1 else "unknown"

So the final program should look like this:

CONVERT = "O-|"

codes = {
    "|": "1+*",
    "-": "2*",
    "O": "2"
}

ops = {
    "*": lambda x, y: x * y,
    "+": lambda x, y: x + y
}

n = ""
for c in CONVERT:
    n += codes.get(c, "")

s = []
for c in n:
    s.append(c)
    if s[-1] in ops:
        if len(s) >= 3:
            o2, o1, o = s[-3:]
            s = s[:-3]
            op = ops[o]
            r = op(int(o1), int(o2))
            s.append(r)
        else:
            s.pop()

print s[0] if len(s) == 1 else "unknown"

And there you have it: a simple program to convert Nanyan numerals to integers.  Try it out with different numerals, and you’ll find it works pretty well.

In future posts, I’ll show you how to go the opposite way—converting integers to Nanyan numerals—and check the results to be certain the program did it right.

Until then, happy coding!


New Project

December 21, 2020 10:04 pm

Since The Ancient Chronicle came out, I’ve been sorting out what my next project will be. I have the next book in the Postdiluvian series more or less mapped out (in my head), and I started working on it, but I have not gotten far on it yet. Instead, I have a very different idea for a book, and I quickly wrote a nearly-complete first draft.

I started revisions several times, but the theming never seemed quite right. I suspect that to an outside observer, the correct course for the story might be obvious. Maybe from the inside of the project I can’t see it. It’s frustrating to see a book or a movie have a flaw that you think should have been obvious to the writers. I just want to be sure this book does not suffer from the same thing.

The problem centers around the motivations of the main character. There really wasn’t a problem with defining what she wanted and what her goals were, but they did not seem to match with what was happening in the story: indeed, the book began with her having just achieved her current goal. Where does she go from there? Well, the story forced her in one direction, but I didn’t think it was clear why she would choose that path. I had an idea recently how to fix it, but it would require more rewrites. I’m currently reevaluating.

Add to this that recently I have been finding it difficult to write. There’s hardly a need to reiterate that it’s been a difficult year, and like many have pointed out, having more time doesn’t necessarily translate to increased productivity, especially during a time of trouble. As such, it may be a while longer before this new project is complete.

But it is my hope that it will be worth the wait.


Recent Posts:

January 2021

December 2020

New Project

December 21, 2020