Coding Nanyan Numerals
January 1, 2021 5:06 pm
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!
Comments