Forums

Sample demo code: Write number in plain english

General discussion about Cobra. Releases and general news will also be posted here.
Feel free to ask questions or just say "Hello".

Sample demo code: Write number in plain english

Postby Csaba » Fri Jan 09, 2009 5:54 am

Hi,

I have written a simple demo program that converts numbers to plain english.
I think this is a good demo program of many fundamental programming aspects.
I hope it fits in the sample collection. Feel free to rewrite it nicer!

Regards
Csaba

Code: Select all
"""
NumberInEnglish  by Csaba Urbaniczky

Purpose: Convert a digit to an string in plain english
Assumptions: -
Effect: Could be set up to extend the integer class with this functionality
Change history: Rewritten version of a Ruby example from 'Learn to Program'
   ' v 1.0 2009-01-09  Csaba Urbaniczky: First version
"""
class ConvertNumberToPlainEnglish

   def numberInEnglish(numberToTranslate as int) as String is shared
        try
            if numberToTranslate == 0
                return "zero" # obvious special case

            numberAsText as String = "" # Here is the the resulting text as the translation is added up

            # Take care of negative numbers
            if numberToTranslate < 0
                numberAsText = "minus "
                numberToTranslate = Math.abs(numberToTranslate)
   
            # These are the binding numbers and the translated binding word
            bindingNumberLookup = [ 1000000000 , 1000000, 1000, 100]
            bindingWordLookup = [ ' billion', ' million', ' thousand', ' hundred',]

            reminder  = numberToTranslate # This is the reminder of the number that will appear as text to the right of a binding word

            # Go through the number for binding words and take care of the left part recursively
            for i in  bindingNumberLookup.count 
                numberToTranslate = reminder // bindingNumberLookup[i] # The left part to the binding word/number like " hundred"     
                reminder = reminder % bindingNumberLookup[i]  # The right part of the binding word/number        
                if numberToTranslate > 0  # Recursion to build up the left part of the number
                    numberAsText +=  .numberInEnglish(numberToTranslate) + bindingWordLookup[i]
                    if reminder > 0
                        numberAsText +=  ' '# So we don't write as millionone....
                       
            # Reminder is now the right part of the binding word and less than 100 - many spacail cases
           
            # Take care of 11 .. 19 teens specials since we can't write 'teenty-two' instead of twelve
            if reminder >= 11 and reminder <= 19
                # Array used to look up english words for numbers, Since array is zero-based, fill "0:th place" with dummy value
                teenagersLookup  = ['zero_never_used', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen']
                return numberAsText + teenagersLookup[reminder - 10] #Finished - return with this special case
   
            # Now 0 .. 10, 20 .. 99 in left to translate to plain english
            numberToTranslate = reminder // 10 # How many tens left, 0 or >=2                 
            reminder = reminder % 10 # Only single digits left now in reminder                                 
            if numberToTranslate > 0  # The tens left
                tensPlaceLookup = ['zero_never_used', 'ten', 'twenty', 'thirty', 'fourty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety']
                numberAsText += tensPlaceLookup[numberToTranslate]
                if reminder > 0
                    numberAsText += "-" # so we don't write 'sixtyfour'
                   
            # Take care of 0 .. 9 that is left to check
            if reminder > 0  # add string if it is larger than zero
                onesPlaceLookup = ['zero_never_used', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'onesPlaceLookupbug']
                return numberAsText + onesPlaceLookup[reminder]
            else
                return numberAsText
               
        catch e as Exception 
            print e
            return numberAsText # end numberInEnglish

   def main is shared # called 'static' in error message
      try
            testNumber= 0
         print '[testNumber] is in english: [.numberInEnglish(testNumber)]'
         testNumber= 5
         print '[testNumber] is in english: [.numberInEnglish(testNumber)]'
            testNumber= 15
         print '[testNumber] is in english: [.numberInEnglish(testNumber)]'
            testNumber= -10
         print '[testNumber] is in english: [.numberInEnglish(testNumber)]'
            testNumber= 42
         print '[testNumber] is in english: [.numberInEnglish(testNumber)]'
            testNumber= 492
         print '[testNumber] is in english: [.numberInEnglish(testNumber)]'
         testNumber= -1492
         print '[testNumber] is in english: [.numberInEnglish(testNumber)]'
            testNumber= 1341492
         print '[testNumber] is in english: [.numberInEnglish(testNumber)]'
            testNumber= 1341492123
         print '[testNumber] is in english: [.numberInEnglish(testNumber)]'
      catch e as Exception
            print e

# End Class
Csaba
 
Posts: 42

Re: Sample demo code: Write number in plain english

Postby Charles » Fri Jan 09, 2009 1:20 pm

Overall looks good.

For the test code, you could loop like so:
for testNumber in [0, 5, 15, -10]
print '[testNumber] is in english: ...

Also, I don't generally "catch e as Exception" and print the exception. I let it propagate and get the printout from the system anyway. Letting them propagate means you can use a debugger or a post-mortem tool like ObjectExplorer-WinForms in the Cobra\Source directory.

I'm also curious about the extra indentation I see in main for "testNumber" assignments. Is that how it looks in your editor?

But overall, nice job.
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Sample demo code: Write number in plain english

Postby Csaba » Sat Jan 10, 2009 3:29 am

Hi alls,
@Chuck, thank you for the input.
No, there where no extra extra indentation in SciTE, but SciTE is not really that good.
Is SharpDevelop useable with Cobra now?
(I usually use VS2005 for VB programming and I have become addicted to autocompletion.)

Regards
Csaba
Csaba
 
Posts: 42

Re: Sample demo code: Write number in plain english

Postby Charles » Sat Jan 10, 2009 6:39 am

There is no SharpDevelop Cobra add-in at this point. I kick started a workspace and some wiki notes, but no one has heeded the battle cry yet. See http://cobra-language.com/forums/viewtopic.php?f=5&t=232.

As the community gets larger, this will inevitably happen, but it takes time for the community to get larger. Especially, before there is IDE integration.

In the mean time, I am constantly busy with the language and compiler. As with most other language communities, it's unlikely that I'll be doing the IDE support.
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Sample demo code: Write number in plain english

Postby Charles » Sat Jan 10, 2009 9:55 am

After working on the code to incorporate it as a sample, I have more constructive feedback below. See the new version which is attached. I'm pretty tired right now, let me know if you see any mistakes or further room for improvement.

-- Instead of:
s as String = ''
You can write:
s = ''

It will still be statically typed as String with proper error checking and full speed.

-- You can put underscores in numbers to make them more readable:

1_000_000_000
1_341_492_123

-- Once you think your output is good, it's best to reformulate it as a "test" that verifies the results. That way when you maintain the code later to add a feature or fix a bug, you will find out if you broke anything.

-- Instead of:
remainder = remainder % 10
use augmented assignment:
remainder %= 10

-- I changed "numberInEnglish" to "numberToEnglish" as "to" is the favored word in existing libraries (at least on .NET) when converting/translating to something. I think it's a good, consistent convention and try to remember it myself.

-- I prefer:
n.abs
over:
Math.abs(n)

-- I added contracts (only 'ensure' in this case). Not overly important in this context, but they don't hurt.

-- I don't say this often, but I thought the local var names were a little long. :-) For example, give the name of the method, the local var "numberAsText" can simply be called "english".

I hope that helps you learn more about Cobra especially in areas that differentiate it from other languages and their styles.
Attachments
EnglishTools.cobra
(5.34 KiB) Downloaded 1577 times
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Sample demo code: Write number in plain english

Postby jonathandavid » Sun Jan 11, 2009 10:03 am

My 2 cents:

1) Can't "System.Collections.IList" be replaced by "Object*" or something of the kind?

[forget it, i've seem it's already listed in the code as a TODO item]

2) I don't like the "shared" block. I'd rather have to write "is shared" in all methods. Even better, I would like Cobra to have a "module" keyword which allowed us to create a sort of class in which everything is shared.

module EnglishTools

def list(items as System.Collections.IList) as String # implicitly shared, because we are in a module
...


Of course, one would not be allowed to create instances of a module, or to inherit from one.

3) I'd like to have a case block, so we could write:

case items.count 
when 0, return ' '
when 1, return items[0]


Note that items.count would only be evaluated once, so no need for a separate "count" variable.

4) The standard library should be complete enough to allow us implement "list" without loops. I'm thinking of something like:

body
count = items.count
if count == 0, return ''
if count == 1, return items[0]

return items.join(', ').replaceLast(',', ' and' )


Here join is a List method that behaves like Python's (note that it calls toString on every item automatically), and replaceLast would be a member of String. Both could be added to .NET BCL classes as extension methods.

5) Can't this loop:

for i in bindingNumberLookup.count
# here use bindingNumberLookup[i]


be rewritten like this:

for bindingNumber in bindingNumberLookup
# simply use bindingNumber

??

6) Why write 1_000_000_000 but then 1000000 and 1000?

7) There are a few typos.

teenty -> twenty
or a stream -> of a stream
spacail -> special

8) I wonder if there shouldn't be a special shortcut syntax for writing function tests when we only want to check some input/output correspondences. I'm thinking of something like:
def list(items as System.Collections.IList) as String
test
checkIOCorrespondence(
[[''], ''],
[['one'], 'one'],
[['a', 'b'], 'a and b'],
[['a', 'b', 'c'], 'a, b and c'],
[[1, 2, 3, 4], '1, 2, 3 and 4'],
)


This would translate to the code in the test code written by Chuck. Of course the term "checkIOCorrespondence" is terrible, but I couldn't think of a better one.

Regards,
Manuel
jonathandavid
 
Posts: 159

Re: Sample demo code: Write number in plain english

Postby Csaba » Sun Jan 11, 2009 11:03 am

Hi

About:
5) Can't this loop:

for i in bindingNumberLookup.count # here use bindingNumberLookup[i]

be rewritten like this:

for bindingNumber in bindingNumberLookup # simply use bindingNumber

In this case, how is bindingWordLookup used? Is it possible to have two iterators, one for each array?
------
I saw another olde errror in
onesPlaceLookup = ['zero_never_used', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'onesPlaceLookupbug']

'onesPlaceLookupbug' is not needed, it is left when I hade single and double qoutes mixed up.

Regards
Csaba
Csaba
 
Posts: 42

Re: Sample demo code: Write number in plain english

Postby jonathandavid » Sun Jan 11, 2009 11:21 am

Csaba wrote:Hi
In this case, how is bindingWordLookup used? Is it possible to have two iteratios, one for each array?


Sorry, I hadn't realized you were using i to index two different containers (wordLookup and numberLookup). I guess in Python one would use the enumerate idiom, but I don't think that's possible to fit into Cobra's syntax:

for i,num in enumerate(bindingNumberLookup)
# here use num, bindingWordLookup(i)


It would really nice to have a syntax to traverse two containers simultaneously, but I don't think that's possible either:

for num,word in bindingNumberLookup,bindingWordLookup
# here use num,word


Those possibilities discarded, the best possibility would probably be to rewrite the code of "numberToEnglish" so that is uses a dictionary:
# These are the binding numbers and the translated binding word 
numToWord = { 1_000_000_000 : ' billion',
1_000_000 : ' million',
1_000 : ' thousand'
100 : ' hundred' }

remainder = n # This is the remainder of the number that will appear as text to the right of a binding word

# Go through the number for binding words and take care of the left part recursively
for num,word in numToWord
n = remainder // bindingNumberLookup[i] # The left part to the binding word/number like ' hundred'
remainder = remainder % num # The right part of the binding word/number
if n > 0 # Recursion to build up the left part of the number
english += .numberToEnglish(n) + word
if remainder > 0
english += ' ' # So we don't write as millionone....


Note, too, that if Cobra had its own version of Python's zip, the dictionary could be built automatically given the bindingNumberLookup and bindingWordLookup lists:

for num,word in Dictionary.zip(bindingNumberLookup,bindingWordLookup)
# use num,word


Of course, building the dictionary ourselves in first place is more efficient and conceptually cleaner.
jonathandavid
 
Posts: 159

Re: Sample demo code: Write number in plain english

Postby jonathandavid » Sun Jan 11, 2009 12:23 pm

Update:

I've learned there are plans to add enumerate(collection) to Cobra, although with a different syntax.

Also,it seemsthat the option I recommended (iterating on a dictionary) does not work in the current version of Cobra. So, basically, forget what I said. The original code that indexed the two lists seems to be the better option until version 0.9.
jonathandavid
 
Posts: 159

Re: Sample demo code: Write number in plain english

Postby Csaba » Sun Jan 11, 2009 3:33 pm

Hi,
I would be nice if this was legal code:
for bindingInt, bindingWord in [ 1_000_000_000 : ' billion', 1_000_000 : ' million', 1000 ' thousand', 100: ' hundred']

Will it be possible in the future then?

Regards
Csaba
Csaba
 
Posts: 42

Next

Return to Discussion

Who is online

Users browsing this forum: No registered users and 13 guests

cron