• The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
Menu
  • The Four Hundred
  • Subscribe
  • Media Kit
  • Contributors
  • About Us
  • Contact
  • Call Again and Again and Again…

    October 5, 2011 Paul Tuohy

    Note: The code accompanying this article is available for download here.

    In programming terms, recursion is the process whereby a function may call itself. Traditionally, this is something we are not used to in RPG. Programs and subroutines cannot call themselves. Or if you did somehow manage it (and you could), you would get unpredictable results.

    But the introduction of subprocedures opened up the possibility of using recursion because subprocedures can call themselves.

    It is usually very difficult to come up with practical examples of using recursion, and most of those examples are usually very specific to an application. For example, a bill of materials process or producing an organizational chart. But I recently came across an old “problem” to which recursion provided an elegant solution: converting numbers to words.

    In this article, I will discuss the mechanics of coding recursive subprocedures and give a practical example of where you might use one.

    But First, A Word of Warning

    As will all powerful programming concepts, you need to be very careful when using recursive calls. If you do not have some logic that breaks the recursive call sequence, you will end up with a never-ending process. As with a never-ending loop, you will be the “dog chasing its tail.”

    The first question you always ask when using recursion is: “How do I get it to stop?”

    Understanding the Problem

    At first glance, the problem seems simple enough. Take a number, segregate it into millions/thousands/hundreds/tens/singles, and put each segregation in words (your challenge will be to include billions). The real problem comes when you go to put a segregation into words, because each segregation may be further segregated. For example, the number 12,345,678,901.23 would be translated to twelve thousand, three hundred and forty-five million, six hundred and seventy-eight thousand, nine hundred and one dollars, and 23 cents. How many millions?

    The process is as follows:

    • Segregate a number into millions, thousands, hundreds, tens, and singles
    • If a segregation is greater than zero, then segregate the segregation into millions, thousands, hundreds, tens, and singles
    • And so on until all digits have been processed

    Let’s look at a program that demonstrates the process. The following code shows the mainline of a program that prompts for a number, calls the currencyToWords() subprocedure, and displays the returned value.

    d forNumber       s             11s 2
    d                 ds
    d  words                       200a   varying
    d  showWords1                   50a   overLay(words : 3)
    d  showWords2                   50a   overLay(words : *next)
    d  showWords3                   50a   overLay(words : *next)
    d  showWords4                   50a   overLay(words : *next)
     /free
      dsply 'Enter number: ' ' ' forNumber;
      words = currencyToWords(forNumber);
      dsply ShowWords1;
      dsply ShowWords2;
      dsply ShowWords3;
      dsply ShowWords4;
      *inLr = *on;
     /end-Free
    

    Now let’s look at some code that shows the currencyToWords() subprocedure that accepts a complete number (including decimals) and two optional parameters to identify the names of the currency and the decimal units (they default to dollars and cents). The procedure extracts the decimal portion of the number, calls the numberToWords() subprocedure to convert the integer portion of requested number into words, and returns a complete description of the requested number as words.

    P currencyToWords...
    P                 B
    d                 PI           200a   varying
    d  number                       13s 2 const
    d  currency                     20a   varying const
    d                                     options(*noPass: *trim)
    d  decimals                     20a   varying const
    d                                     options(*noPass: *trim)
    
    d forCurrency     s             20a   varying inz('Dollars')
    d forDecimals     s             20a   varying inz('cents')
    
    d cents           s              2s 0
    
     /free
      if (%parms() > 1);
         forCurrency = currency;
      endIf;
      if (%parms() > 2);
         forDecimals = decimals;
      endIf;
      cents = %int((number - %int(number)) * 100);
      return numberToWords(%int(number)) + ' ' + forCurrency + 
             ' and ' + %editC(cents: 'X') + ' '  + forDecimals; 
    
     /end-Free
    P                 E
    

    Finally, the next piece of code shows the numberToWords() subprocedure, which returns a text description of a requested integer.

    The D specs contain the definition of:

    • Work fields for the segregated portions of the number (millions, thousands, hundreds, tens, and singles)
    • The field that will contain the return value
    • The definition of arrays (oneWords and tenWords) for all required words. Note that varying fields are used to eliminate the requirement for trimming when constructing the return text

    The process in the subprocedure is as follows:

    • Segregate the number into its component parts (millions, thousands, hundreds, tens, and singles)
    • For millions, thousands, and hundreds, if the value is greater than 0, make a recursive call to the subprocedure (the subprocedure calls itself) to convert the segregated amount to words
    • Convert the tens and units to words
    • Return the result

    The recursive calls cease when all segregated portions are zero, which has to happen since the number is being constantly segregated.

    P numberToWords...
    P                 B
    
    d                 PI           200a   varying
    d  numberIn                      9s 0 const
    
    d number          s             10i 0
    d mega            s             10i 0
    d kilo            s             10i 0
    d hundreds        s             10i 0
    d tens            s             10i 0
    d singles         s             10i 0
    
    d result          s            200a   varying
    
    d forOneWords     ds
    d                                9a   varying inz('One')
    d                                9a   varying inz('Two')
    d                                9a   varying inz('Three')
    d                                9a   varying inz('Four')
    d                                9a   varying inz('Five')
    d                                9a   varying inz('Six')
    d                                9a   varying inz('Seven')
    d                                9a   varying inz('Eight')
    d                                9a   varying inz('Nine')
    d                                9a   varying inz('Ten')
    d                                9a   varying inz('Eleven')
    d                                9a   varying inz('Twelve')
    d                                9a   varying inz('Thirteen')
    d                                9a   varying inz('Fourteen')
    d                                9a   varying inz('Fifteen')
    d                                9a   varying inz('Sixteen')
    d                                9a   varying inz('Seventeen')
    d                                9a   varying inz('Eighteen')
    d                                9a   varying inz('Nineteen')
    d  oneWords                      9a   varying dim(19)
    d                                     overlay(forOneWords)
    
    d forTenWords     ds
    d                                7a   varying inz('')
    d                                7a   varying inz('Twenty')
    d                                7a   varying inz('Thirty')
    d                                7a   varying inz('Forty')
    d                                7a   varying inz('Fifty')
    d                                7a   varying inz('Sixty')
    d                                7a   varying inz('Seventy')
    d                                7a   varying inz('Eighty')
    d                                7a   varying inz('Ninety')
    d  tenWords                      7a   varying dim(9) 
    d                                     overlay(forTenWords)
    
     /free
      number = numberIn;
      mega = %int(number / 1000000);
      number -= mega * 1000000;
      kilo = %int(number / 1000);
      number -= kilo * 1000;
      hundreds = %int(number / 100);
      number -= hundreds * 100;
      tens = %int(number / 10);
      singles = number - (tens * 10);
    
      result = '';
    
      if (mega > 0) ;
         result += numberToWords(mega) + ' Million ';
      endIf;
      if (kilo > 0) ;
         result += numberToWords(kilo) + ' Thousand ';
      endIf;
      if (hundreds > 0) ;
         result += numberToWords(hundreds) + ' Hundred ';
      endIf;
    
    
      if (tens > 0 or singles > 0);
    
         if (result <> '');
            result += 'and ';
         endIf;
    
         if (tens < 2);
            result += oneWords(tens * 10 + singles);
         else;
            result += tenWords(tens);
            if (singles <> 0);
              result += ' ' + oneWords(singles);
            endIf;
         endIf;
    
      endIf;
    
      if (result = '');
         result = 'zero';
      endIf;
      return result;
     /end-Free
    P                 E
    

    There You Have It

    Although not something you might be using on a daily basis, recursive calls are a powerful tool to have in your toolbox. Just remember, always know how to break the recursive loop.

    Paul Tuohy is CEO of ComCon, an iSeries consulting company, and is one of the co-founders of System i Developer, which hosts the RPG & DB2 Summit conferences. He is an award-winning speaker who also speaks regularly at COMMON conferences, and is the author of “Re-engineering RPG Legacy Applications,” “The Programmers Guide to iSeries Navigator,” and the self-study course called “iSeries Navigator for Programmers.” Send your questions or comments for Paul to Ted Holt via the IT Jungle Contact page.



                         Post this story to del.icio.us
                   Post this story to Digg
        Post this story to Slashdot

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Tags:

    Sponsored by
    Fresche Solutions

    Move Projects Forward with Expert Staffing Services

    Gain access to IBM i experts to move IT projects forward, reduce backlog and support business-critical systems.

    Fast onboarding, flexible engagement models for IBM i, RPG, COBOL, CA 2E (Synon), ERPs and more:

    • Bug Fixes & Maintenance
    • Full-Stack Web and Mobile Development
    • Application Enhancements
    • Application Maintenance
    • Database Modernization

    Speak to an Expert »

    Share this:

    • Reddit
    • Facebook
    • LinkedIn
    • Twitter
    • Email

    Sponsored Links

    System i Developer:  Join the Gurus at the RPG & DB2 Summit in St. Louis, October 17-19
    The 400 School:  LIVE Online Training RPG IV & COBOL Boot Camps
    Shield Advanced Solutions:  Access IBM i data & objects from Linux & Windows Servers using PHP

    IT Jungle Store Top Book Picks

    BACK IN STOCK: Easy Steps to Internet Programming for System i: List Price, $49.95

    The iSeries Express Web Implementer's Guide: List Price, $49.95
    The iSeries Pocket Database Guide: List Price, $59
    The iSeries Pocket SQL Guide: List Price, $59
    The iSeries Pocket WebFacing Primer: List Price, $39
    Migrating to WebSphere Express for iSeries: List Price, $49
    Getting Started with WebSphere Express for iSeries: List Price, $49
    The All-Everything Operating System: List Price, $35
    The Best Joomla! Tutorial Ever!: List Price, $19.95

    CYBRA Signs European Distributor for RFID and Barcode Products IBM Readies October Power Systems Announcements

    Leave a Reply Cancel reply

Volume 11, Number 29 -- October 5, 2011
THIS ISSUE SPONSORED BY:

SEQUEL Software
WorksRight Software
The 400 School

Table of Contents

  • Call Again and Again and Again…
  • Another Way to Pass Parms to SBMJOB
  • Admin Alert: How to Retrieve Password Parameters for Auditors
  • Call Again and Again and Again…
  • Another Way to Pass Parms to SBMJOB
  • Admin Alert: How to Retrieve Password Parameters for Auditors

Content archive

  • The Four Hundred
  • Four Hundred Stuff
  • Four Hundred Guru

Recent Posts

  • IBM Tweaks Some Power Systems Prices Down, Others Up
  • Disaster Recovery: From OS/400 V5R3 To IBM i 7.4 In 36 Hours
  • The Disconnect In Modernization Planning And Execution
  • Superior Support: One Of The Reasons You Pay The Power Systems Premium
  • IBM i PTF Guide, Volume 25, Number 13
  • IBM i Has a Future ‘If Kept Up To Date,’ IDC Says
  • When You Need Us, We Are Ready To Do Grunt Work
  • Generative AI: Coming to an ERP Near You
  • Four Hundred Monitor, March 22
  • IBM i PTF Guide, Volume 25, Number 12

Subscribe

To get news from IT Jungle sent to your inbox every week, subscribe to our newsletter.

Pages

  • About Us
  • Contact
  • Contributors
  • Four Hundred Monitor
  • IBM i PTF Guide
  • Media Kit
  • Subscribe

Search

Copyright © 2023 IT Jungle