Finding/Modifying the last occurrence  
 

 Post a new Code Sample   Modify this Code Sample

This assemblage of programs addresses an interesting problem. How best to locate and change
the last occurrence of a character in an alpha variable.


* FINDING THE LAST ASTERISK.
*
* THIS PROGRAM WILL NOT RUN ON A MAINFRAME.
* THE "DIRECTION BACKWARD" CLAUSE FOR
* THE EXAMINE STATEMENT IS NOT VALID ON
* A MAINFRAME.
*
* THE CLAUSE IS VALID ON THE PC, WHERE THIS PROGRAM
* WAS RUN
*
DEFINE DATA LOCAL
1 #A (A30) INIT <'QWRET*SSSS*BVCX*KLMN*QWER'>
1 #POS (I2)
END-DEFINE
*
INCLUDE AATITLER
INCLUDE AASETC
*
WRITE 5T 'BEFORE EXAMINE' 20T '12345678901234567890123456789'
                        /  20T #A  // 
*
EXAMINE DIRECTION BACKWARD #A FOR '*'
    REPLACE FIRST WITH '?' GIVING POSITION #POS
*
* 'FIRST', WHEN EXAMINE'ING BACKWARD, IS ACTUALLY 'LAST'.
* SYNTAX IS 'BACKWARD'. 'BACKWARDS' WILL GET YOU A SYNTAX ERROR.
*
WRITE 5T 'AFTER EXAMINE' 20T '12345678901234567890123456789'
         /  20T #A //
         20T 'LAST ASTERISK WAS AT POSITION:' #POS (EM=99) 
END

*******************************************************
Output from the program above


    PAGE #   1                    DATE:    02/26/11
    PROGRAM: LAST01               LIBRARY: SNIPPET
 
    BEFORE EXAMINE 12345678901234567890123456789
                   QWRET*SSSS*BVCX*KLMN*QWER
 
 
    AFTER EXAMINE  12345678901234567890123456789
                   QWRET*SSSS*BVCX*KLMN?QWER
 
                   LAST ASTERISK WAS AT POSITION: 21   
--------------------------------------------------------
Note that we correctly found, and changed, the asterisk
that was at position 21.
********************************************************

* FINDING THE LAST ASTERISK.
*
* THIS PROGRAM WILL RUN ON A MAINFRAME AND A PC.
* HOWEVER, THE CODE WITH THE "DIRECTION BACKWARD"
* CLAUSE SHOWN ABOVE IS MUCH FASTER ON THE PC.
*
DEFINE DATA LOCAL
1 #A (A30) INIT <'QWRET*SSSS*BVCX*KLMN*QWER'>
1 #B (A30)
1 #POS (I2)
1 #LENGTH (I2)
1 #AA (A5) INIT <'ABC'>
1 #BB (A10)
END-DEFINE
*
INCLUDE AATITLER
INCLUDE AASETC
*
MOVE #A (PM=I) TO #B
* 
* AS SHOWN IN THE FOLLOWING WRITE, THE OPTION (PM=I)
* REVERSES THE CHARACTERS IN THE SOURCE FIELD (#A)
* AND PLACES THE RESULT IN OUR TARGET FIELD (#B).
* NOTE: TRAILING BLANKS IN THE SOURCE FIELD (#A) ARE IGNORED.
* THAT IS, THEY DO NOT CREATE LEADING BLANKS IN THE
* TARGET (#B).
*
WRITE 20T 'BEFORE EXAMINE' // 10T 'POSITION' 20T '12345678901234567890123456789'
                        / 12T '#A==>' 20T #A 
                        / 12T '#B==>' 20T #B / 12T '-' (30) 
*
EXAMINE #B FOR '*'
    REPLACE FIRST WITH '?' GIVING POSITION #POS
                           GIVING LENGTH #LENGTH
* 
* ABOVE, WE ARE EXAMINE'ING #B. NOTE, THE FIRST ASTERISK IN #B
* WILL BE THE LAST ASTERISK IN #A. HENCE, WE WILL REPLACE THE 
* FIRST ASTERISK IN #B, WHICH WILL END UP AS THE LAST ASTERISK
* IN #A.
* NOTE THAT #LENGTH HAS THE NUMBER OF CHARACTERS (NOT COUNTING
* TRAILING BLANKS) IN BOTH #A AND #B.
* #POS IS THE POSITION OF THE FIRST ASTERISK IN #B. THIS
* IS NOT THE VALUE WE REQUIRE (POSITION OF LAST ASTERISK IN #A).
* A COMPUTE BELOW WILL SUFFICE TO GENERATE THE REQUIRED VALUE.                           
*
WRITE 20T 'AFTER EXAMINE' // 10T 'POSITION' 20T '12345678901234567890123456789'
         /  12T '#B==>' 20T #B //
         10T 'DATA FROM EXAMINE - POSITION:' #POS (EM=99) 'LENGTH:' #LENGTH (EM=99) /
         12T '-' (30) 
*   
MOVE  #B (PM=I) TO #A
*
* HAVING CHANGED THE FIRST ASTERISK IN #B TO A QUESTION MARK,
* WE NOW CAN REVERSE #B TO RE-CREATE #A, BUT WITH THE
* LAST ASTERISK CHANGED TO A QUESTION MARK.
*   
COMPUTE #POS = #LENGTH - #POS + 1
*
* THE COMPUTATION ABOVE "LOCATES" THE POSITION OF THE CHANGED CHARACTER
* WE COULD ALSO HAVE USED THE NEW VALUE OF #POS TO CHANGE THE
* SINGLE CHARACTER IN #A. THIS WILL BE SHOWN IN THE FOLLOWING
* PROGRAM WHICH CONTAINS PERFORMANCE COMPARISONS.
*
WRITE 20T 'AFTER MOVE(PM=I)' // 10T 'POSITION' 20T '12345678901234567890123456789'
         /  12T '#A==>' 20T #A //
         10T 'LAST ASTERISK WAS AT POSITION:' #POS (EM=99) 
*      
END

**********************************************************
Here is the output for the above program:
 
 
    PAGE #   1                    DATE:    02/26/11
    PROGRAM: LAST02               LIBRARY: SNIPPET
 
                   BEFORE EXAMINE
 
         POSITION  12345678901234567890123456789
           #A==>   QWRET*SSSS*BVCX*KLMN*QWER
           #B==>   REWQ*NMLK*XCVB*SSSS*TERWQ
           ------------------------------
                   AFTER EXAMINE
 
         POSITION  12345678901234567890123456789
           #B==>   REWQ?NMLK*XCVB*SSSS*TERWQ
 
         DATA FROM EXAMINE - POSITION: 05 LENGTH: 25
           ------------------------------
                   AFTER MOVE(PM=I)
 
         POSITION  12345678901234567890123456789
           #A==>   QWRET*SSSS*BVCX*KLMN?QWER
 
         LAST ASTERISK WAS AT POSITION: 21

***************************************************
Now for timing comparisons

* FINDING THE LAST ASTERISK.
* THIS PROGRAM WILL NOT RUN ON A MAINFRAME.
* THE "DIRECTION BACKWARD" CLAUSE FOR
* THE EXAMINE STATEMENT IS NOT VALID ON
* A MAINFRAME.
*
* THE CLAUSE IS VALID ON THE PC, WHERE THIS PROGRAM
* WAS RUN
*
* THIS TIMING COMPARISON IS NOT QUITE IDENTICAL TO
* THE PROBLEM DESCRIBED IN THE PRECEDING PROGRAMS.
* IT DOES NOT INCLUDE CHANGING THE LAST ASTERISK,
* MERELY LOCATING THE LAST ASTERISK.
*
DEFINE DATA LOCAL
1 #A (A30) INIT <'QWRET*SSSS*BVCX*KLMN*QWER'>
1 #B (A30)
1 #POS (I2)
1 #LENGTH (I2)
1 #LOOP (I4)
1 #CPU-START (I4)
1 #CPU-ELAPSED (I4)
END-DEFINE
*
INCLUDE AATITLER
INCLUDE AASETC
*
MOVE *CPU-TIME TO #CPU-START
SETA. SETTIME
FOR #LOOP = 1 TO 1000000
EXAMINE DIRECTION BACKWARD #A FOR '*'
    GIVING POSITION #POS
END-FOR
COMPUTE #CPU-ELAPSED = *CPU-TIME - #CPU-START
WRITE 5T 'BACKWARD TIME' 25T *TIMD (SETA.) #CPU-ELAPSED    
*
MOVE *CPU-TIME TO #CPU-START
SETB. SETTIME
FOR #LOOP = 1 TO 1000000
MOVE #A (PM=I) TO #B
EXAMINE #B FOR '*'
      GIVING POSITION #POS
      GIVING LENGTH #LENGTH
COMPUTE #POS = #LENGTH - #POS + 1      
END-FOR
COMPUTE #CPU-ELAPSED = *CPU-TIME - #CPU-START
WRITE 5T 'PM=I TIME' 25T *TIMD (SETB.) #CPU-ELAPSED    
*
MOVE *CPU-TIME TO #CPU-START
SETC. SETTIME
FOR #LOOP = 1 TO 1000000
IGNORE   
END-FOR
COMPUTE #CPU-ELAPSED = *CPU-TIME - #CPU-START
WRITE 5T 'DUMMY LOOP' 25T *TIMD (SETC.) #CPU-ELAPSED    
*
END

***************************************************
Here is the output for the timing comparison program

    PAGE #   1                    DATE:    02/26/11
    PROGRAM: LAST03               LIBRARY: SNIPPET
 
    BACKWARD TIME              9          85
    PM=I TIME                 21         209
    DUMMY LOOP                 3          25

Note that the MOVE (PM=I) and the COMPUTE make the
PM=I time about three times as long as the BACKWARD
time (after subtracting the DUMMY FOR loop from
both times).

However, the PM=I time is considerably faster than any
mainframe code that does not use PM=I.
****************************************************

* FINDING THE LAST ASTERISK.
* THIS PROGRAM WILL NOT RUN ON A MAINFRAME.
* THE "DIRECTION BACKWARD" CLAUSE FOR
* THE EXAMINE STATEMENT IS NOT VALID ON
* A MAINFRAME.
*
* THE CLAUSE IS VALID ON THE PC, WHERE THIS PROGRAM
* WAS RUN
*
* THIS TIMING COMPARISON COMPARES THE TIME TO
* CHANGE THE LAST ASTERISK TO A QUESTION MARK.
* 
* FOR THE "BACKWARD OPTION" (PC ONLY) WE DO NOT HAVE
* TO USE THE GIVING POSITION OPTION.
*
* FOR THE "PM=I)" OPTION, WE HAVE TWO "EXTRA" MOVE'S
* AND A COMPUTE.
*
* IMPORTANT NOTE: IN ORDER TO REPRODUCE THE SAME OPERATIONS
* EVERY ITERATION, WE MUST NOT 
*
DEFINE DATA LOCAL
1 #A-FILL (A30) INIT <'QWRET*SSSS*BVCX*KLMN*QWER'>
1 #A (A30) INIT <'QWRET*SSSS*BVCX*KLMN*QWER'>
1 REDEFINE #A
  2 #A-ARRAY (A1/1:30)
1 #B (A30)
1 #POS (I2)
1 #LENGTH (I2)
1 #LOOP (I4)
1 #CPU-START (I4)
1 #CPU-ELAPSED (I4)
END-DEFINE
*
INCLUDE AATITLER
INCLUDE AASETC
*
MOVE *CPU-TIME TO #CPU-START
SETA. SETTIME
FOR #LOOP = 1 TO 1000000
MOVE #A-FILL TO #A
EXAMINE DIRECTION BACKWARD #A FOR '*'
    REPLACE FIRST WITH '?'
END-FOR
COMPUTE #CPU-ELAPSED = *CPU-TIME - #CPU-START
WRITE 5T 'BACKWARD TIME' 25T *TIMD (SETA.) #CPU-ELAPSED    
*
MOVE *CPU-TIME TO #CPU-START
SETB. SETTIME
FOR #LOOP = 1 TO 1000000
MOVE #A-FILL TO #A
MOVE #A (PM=I) TO #B
EXAMINE #B FOR '*'
      GIVING POSITION #POS
      GIVING LENGTH #LENGTH
COMPUTE #POS = #LENGTH - #POS + 1 
MOVE '?' TO #A-ARRAY (#POS)     
END-FOR
COMPUTE #CPU-ELAPSED = *CPU-TIME - #CPU-START
WRITE 5T 'PM=I TIME' 25T *TIMD (SETB.) #CPU-ELAPSED    
*
MOVE *CPU-TIME TO #CPU-START
SETC. SETTIME
FOR #LOOP = 1 TO 1000000
MOVE #A-FILL TO #A
END-FOR
COMPUTE #CPU-ELAPSED = *CPU-TIME - #CPU-START
WRITE 5T 'DUMMY LOOP' 25T *TIMD (SETC.) #CPU-ELAPSED    
*
END

**************************************************
Output for the above program.


    PAGE #   1                    DATE:    02/26/11
    PROGRAM: LAST04               LIBRARY: SNIPPET
 
    BACKWARD TIME             15         148
    PM=I TIME                 29         287
    DUMMY LOOP                 4          40

As with the last timing comparison (TIME03), the BACKWARD
time is less than half the PM=I time.
****************************************************
                         

Description :

On a PC, there is a powerful option for the EXAMINE statement which "examines" the target variable
right to left rather than left to right. This option does not exist on the mainframe.

There is a little known option, PM=I, which can reverse the order of characters in a variable. This is
the fastest way to perform this function on a mainframe.

Summary

On a PC, use EXAMINE BACKWARD, on a mainframe, use PM=I.


Disclaimer :

Utilities and samples shown here are not official parts of the Software AG products. These utilities and samples are not eligible for technical support through Software AG Customer Care. Software AG makes no guarantees pertaining to the functionality, scalability, robustness, or degree of testing of these utilities and samples. Customers are strongly advised to consider these utilities and samples as "working examples" from which they should build and test their own solutions

 
Search Community Websites
Natural Products
Roadmap (Aug, 2012)
NaturalONE
Natural General
Natural for Mainframes
Natural for Linux, Unix and OpenVMS
Natural for Windows
Natural Add-Ons
Terms of Use    Privacy Policy    Imprint    Copyright © 2012 Software AG    Contact Us