Recursion with Macro ;-)

classic Classic list List threaded Threaded
20 messages Options
Reply | Threaded
Open this post in threaded view
|

Recursion with Macro ;-)

David Marso
Administrator
Gene commented on an earlier thread mentioning that my posts sometimes use recursion.
I replied that they didn't and that SPSS didn't really support recursion.
After a bit of screwing around I came up with an example of a simple recursive MACRO.
UNFORTUNATELY:  I can't think of any way to do anything useful with the concept ;-(
If I could come up with something useful there would undoubtedly be more efficient ways of implementing any resulting algorithm (iterative).
Kirill?????
---
DEFINE RECURSE (!POS !TOKENS(1)).
!IF (!LENGTH(!1) !LT 99) !THEN
!LET !X=!CONCAT(!1,"X")
!CONCAT (RECURSE," ",!X) .
ECHO !QUOTE (!1).
!IFEND
!ENDDEFINE.
SET MNEST 100.
RECURSE X .
-Output-
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXX
XXXXXXXXXXXXXX
XXXXXXXXXXXXX
XXXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXX
XXXXXXXXX
XXXXXXXX
XXXXXXX
XXXXXX
XXXXX
XXXX
XXX
XX
X
Please reply to the list and not to my personal email.
Those desiring my consulting or training services please feel free to email me.
---
"Nolite dare sanctum canibus neque mittatis margaritas vestras ante porcos ne forte conculcent eas pedibus suis."
Cum es damnatorum possederunt porcos iens ut salire off sanguinum cliff in abyssum?"
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

Bruce Weaver
Administrator
You wanted examples of posts that sent me to the FM.  See below!

This is from the FM entry for SET:

Macro Displays.  You can use the MEXPAND, MITERATE, and MNEST subcommands to control macro expansion, the maximum number of loop iterations, and nesting levels within a macro.  You can also use the MPRINT subcommands to control the display of the variables, commands, and parameters that a macro uses.


p.s. - I can see that macro (and variations thereon) being used to produce ASCII art.  


David Marso wrote
Gene commented on an earlier thread mentioning that my posts sometimes use recursion.
I replied that they didn't and that SPSS didn't really support recursion.
After a bit of screwing around I came up with an example of a simple recursive MACRO.
UNFORTUNATELY:  I can't think of any way to do anything useful with the concept ;-(
If I could come up with something useful there would undoubtedly be more efficient ways of implementing any resulting algorithm (iterative).
Kirill?????
---
DEFINE RECURSE (!POS !TOKENS(1)).
!IF (!LENGTH(!1) !LT 99) !THEN
!LET !X=!CONCAT(!1,"X")
!CONCAT (RECURSE," ",!X) .
ECHO !QUOTE (!1).
!IFEND
!ENDDEFINE.
SET MNEST 100.
RECURSE X .
-Output-
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXX
XXXXXXXXXXXXXX
XXXXXXXXXXXXX
XXXXXXXXXXXX
XXXXXXXXXXX
XXXXXXXXXX
XXXXXXXXX
XXXXXXXX
XXXXXXX
XXXXXX
XXXXX
XXXX
XXX
XX
X
--
Bruce Weaver
bweaver@lakeheadu.ca
http://sites.google.com/a/lakeheadu.ca/bweaver/

"When all else fails, RTFM."

PLEASE NOTE THE FOLLOWING: 
1. My Hotmail account is not monitored regularly. To send me an e-mail, please use the address shown above.
2. The SPSSX Discussion forum on Nabble is no longer linked to the SPSSX-L listserv administered by UGA (https://listserv.uga.edu/).
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

David Marso
Administrator
OK Bruce,
I don't want to see any green junk flying after your head stops spinning ;-)))
LOL

DEFINE RECURSE (!POS !TOKENS(1) /!POS !TOKENS(1) / !POS !CMDEND).
!IF (!TAIL(!3) !NE !NULL) !THEN
!1 !HEAD(!3) !2 !TAIL(!3) .
RECURSE  !1 !2 !TAIL(!3) .
!IFEND
!ENDDEFINE.

SET MNEST 100.
DATA LIST FREE /A B C D E F G H I J K L M .
BEGIN DATA
1 1 5 3 1 3 6 1 5 3 1 5 3
6 2 6 1 5 3 2 5 3 6 5 2 3
5 1 2 5 6 3 5 1 5 3 6 2 5
3 2 1 5 3 6 1 2 5 3 1 2 5
3 5 6 1 2 5 6 3 7 3 5 1 2
4 5 6 3 2 6 7 5 3 1 2 3 4  
END DATA.
RECURSE CROSSTABS BY A B C D E F G H I J K L M.
RECURSE CORRELATION WITH A B C D E F G H I J K L M.
Please reply to the list and not to my personal email.
Those desiring my consulting or training services please feel free to email me.
---
"Nolite dare sanctum canibus neque mittatis margaritas vestras ante porcos ne forte conculcent eas pedibus suis."
Cum es damnatorum possederunt porcos iens ut salire off sanguinum cliff in abyssum?"
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

Bruce Weaver
Administrator
For the benefit of those who don't have a current SPSS license (or who just have not bothered to run the syntax), here is what David's magical macro generates (obtained by setting MPRINT ON before running the macros):

SET MPRINT ON.
RECURSE CROSSTABS BY A B C D E F G H I J K L M.
2144  0 M>  
2145  0 M>  .
2146  0 M>  CROSSTABS A BY B C D E F G H I J K L M.
2147  0 M>  
2148  0 M>  .
2149  0 M>  CROSSTABS B BY C D E F G H I J K L M.
2150  0 M>  
2151  0 M>  .
2152  0 M>  CROSSTABS C BY D E F G H I J K L M.
2153  0 M>  
2154  0 M>  .
2155  0 M>  CROSSTABS D BY E F G H I J K L M.
2156  0 M>  
2157  0 M>  .
2158  0 M>  CROSSTABS E BY F G H I J K L M.
2159  0 M>  
2160  0 M>  .
2161  0 M>  CROSSTABS F BY G H I J K L M.
2162  0 M>  
2163  0 M>  .
2164  0 M>  CROSSTABS G BY H I J K L M.
2165  0 M>  
2166  0 M>  .
2167  0 M>  CROSSTABS H BY I J K L M.
2168  0 M>  
2169  0 M>  .
2170  0 M>  CROSSTABS I BY J K L M.
2171  0 M>  
2172  0 M>  .
2173  0 M>  CROSSTABS J BY K L M.
2174  0 M>  
2175  0 M>  .
2176  0 M>  CROSSTABS K BY L M.
2177  0 M>  
2178  0 M>  .
2179  0 M>  CROSSTABS L BY M.
2180  0 M>  

RECURSE CORRELATION WITH A B C D E F G H I J K L M.

2196  0 M>  
2197  0 M>  .
2198  0 M>  CORRELATION A WITH B C D E F G H I J K L M.
2199  0 M>  
2200  0 M>  .
2201  0 M>  CORRELATION B WITH C D E F G H I J K L M.
2202  0 M>  
2203  0 M>  .
2204  0 M>  CORRELATION C WITH D E F G H I J K L M.
2205  0 M>  
2206  0 M>  .
2207  0 M>  CORRELATION D WITH E F G H I J K L M.
2208  0 M>  
2209  0 M>  .
2210  0 M>  CORRELATION E WITH F G H I J K L M.
2211  0 M>  
2212  0 M>  .
2213  0 M>  CORRELATION F WITH G H I J K L M.
2214  0 M>  
2215  0 M>  .
2216  0 M>  CORRELATION G WITH H I J K L M.
2217  0 M>  
2218  0 M>  .
2219  0 M>  CORRELATION H WITH I J K L M.
2220  0 M>  
2221  0 M>  .
2222  0 M>  CORRELATION I WITH J K L M.
2223  0 M>  
2224  0 M>  .
2225  0 M>  CORRELATION J WITH K L M.
2226  0 M>  
2227  0 M>  .
2228  0 M>  CORRELATION K WITH L M.
2229  0 M>  
2230  0 M>  .
2231  0 M>  CORRELATION L WITH M.
2232  0 M>  

SET MPRINT OFF.
2248  0 M>  SET MPRINT OFF.


That's pretty slick.  ;-)


David Marso wrote
OK Bruce,
I don't want to see any green junk flying after your head stops spinning ;-)))
LOL

DEFINE RECURSE (!POS !TOKENS(1) /!POS !TOKENS(1) / !POS !CMDEND).
!IF (!TAIL(!3) !NE !NULL) !THEN
!1 !HEAD(!3) !2 !TAIL(!3) .
RECURSE  !1 !2 !TAIL(!3) .
!IFEND
!ENDDEFINE.

SET MNEST 100.
DATA LIST FREE /A B C D E F G H I J K L M .
BEGIN DATA
1 1 5 3 1 3 6 1 5 3 1 5 3
6 2 6 1 5 3 2 5 3 6 5 2 3
5 1 2 5 6 3 5 1 5 3 6 2 5
3 2 1 5 3 6 1 2 5 3 1 2 5
3 5 6 1 2 5 6 3 7 3 5 1 2
4 5 6 3 2 6 7 5 3 1 2 3 4  
END DATA.
RECURSE CROSSTABS BY A B C D E F G H I J K L M.
RECURSE CORRELATION WITH A B C D E F G H I J K L M.
--
Bruce Weaver
bweaver@lakeheadu.ca
http://sites.google.com/a/lakeheadu.ca/bweaver/

"When all else fails, RTFM."

PLEASE NOTE THE FOLLOWING: 
1. My Hotmail account is not monitored regularly. To send me an e-mail, please use the address shown above.
2. The SPSSX Discussion forum on Nabble is no longer linked to the SPSSX-L listserv administered by UGA (https://listserv.uga.edu/).
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

Maguin, Eugene
Bruce, thanks for posting the expansion.
David, thanks for imagining and illustrating what is possible.
Gene Maguin

-----Original Message-----
From: SPSSX(r) Discussion [mailto:[hidden email]] On Behalf Of Bruce Weaver
Sent: Monday, December 09, 2013 4:40 PM
To: [hidden email]
Subject: Re: Recursion with Macro ;-)

For the benefit of those who don't have a current SPSS license (or who just have not bothered to run the syntax), here is what David's magical macro generates (obtained by setting MPRINT ON before running the macros):

SET MPRINT ON.
RECURSE CROSSTABS BY A B C D E F G H I J K L M.
2144  0 M>
2145  0 M>  .
2146  0 M>  CROSSTABS A BY B C D E F G H I J K L M.
2147  0 M>
2148  0 M>  .
2149  0 M>  CROSSTABS B BY C D E F G H I J K L M.
2150  0 M>
2151  0 M>  .
2152  0 M>  CROSSTABS C BY D E F G H I J K L M.
2153  0 M>
2154  0 M>  .
2155  0 M>  CROSSTABS D BY E F G H I J K L M.
2156  0 M>
2157  0 M>  .
2158  0 M>  CROSSTABS E BY F G H I J K L M.
2159  0 M>
2160  0 M>  .
2161  0 M>  CROSSTABS F BY G H I J K L M.
2162  0 M>
2163  0 M>  .
2164  0 M>  CROSSTABS G BY H I J K L M.
2165  0 M>
2166  0 M>  .
2167  0 M>  CROSSTABS H BY I J K L M.
2168  0 M>
2169  0 M>  .
2170  0 M>  CROSSTABS I BY J K L M.
2171  0 M>
2172  0 M>  .
2173  0 M>  CROSSTABS J BY K L M.
2174  0 M>
2175  0 M>  .
2176  0 M>  CROSSTABS K BY L M.
2177  0 M>
2178  0 M>  .
2179  0 M>  CROSSTABS L BY M.
2180  0 M>

RECURSE CORRELATION WITH A B C D E F G H I J K L M.

2196  0 M>
2197  0 M>  .
2198  0 M>  CORRELATION A WITH B C D E F G H I J K L M.
2199  0 M>
2200  0 M>  .
2201  0 M>  CORRELATION B WITH C D E F G H I J K L M.
2202  0 M>
2203  0 M>  .
2204  0 M>  CORRELATION C WITH D E F G H I J K L M.
2205  0 M>
2206  0 M>  .
2207  0 M>  CORRELATION D WITH E F G H I J K L M.
2208  0 M>
2209  0 M>  .
2210  0 M>  CORRELATION E WITH F G H I J K L M.
2211  0 M>
2212  0 M>  .
2213  0 M>  CORRELATION F WITH G H I J K L M.
2214  0 M>
2215  0 M>  .
2216  0 M>  CORRELATION G WITH H I J K L M.
2217  0 M>
2218  0 M>  .
2219  0 M>  CORRELATION H WITH I J K L M.
2220  0 M>
2221  0 M>  .
2222  0 M>  CORRELATION I WITH J K L M.
2223  0 M>
2224  0 M>  .
2225  0 M>  CORRELATION J WITH K L M.
2226  0 M>
2227  0 M>  .
2228  0 M>  CORRELATION K WITH L M.
2229  0 M>
2230  0 M>  .
2231  0 M>  CORRELATION L WITH M.
2232  0 M>

SET MPRINT OFF.
2248  0 M>  SET MPRINT OFF.


That's pretty slick.  ;-)



David Marso wrote

> OK Bruce,
> I don't want to see any green junk flying after your head stops
> spinning
> ;-)))
> LOL
>
> DEFINE RECURSE (!POS !TOKENS(1) /!POS !TOKENS(1) / !POS !CMDEND).
> !IF (!TAIL(!3) !NE !NULL) !THEN
> !1 !HEAD(!3) !2 !TAIL(!3) .
> RECURSE  !1 !2 !TAIL(!3) .
> !IFEND
> !ENDDEFINE.
>
> SET MNEST 100.
> DATA LIST FREE /A B C D E F G H I J K L M .
> BEGIN DATA
> 1 1 5 3 1 3 6 1 5 3 1 5 3
> 6 2 6 1 5 3 2 5 3 6 5 2 3
> 5 1 2 5 6 3 5 1 5 3 6 2 5
> 3 2 1 5 3 6 1 2 5 3 1 2 5
> 3 5 6 1 2 5 6 3 7 3 5 1 2
> 4 5 6 3 2 6 7 5 3 1 2 3 4
> END DATA.
> RECURSE CROSSTABS BY A B C D E F G H I J K L M.
> RECURSE CORRELATION WITH A B C D E F G H I J K L M.





-----
--
Bruce Weaver
[hidden email]
http://sites.google.com/a/lakeheadu.ca/bweaver/

"When all else fails, RTFM."

NOTE: My Hotmail account is not monitored regularly.
To send me an e-mail, please use the address shown above.

--
View this message in context: http://spssx-discussion.1045642.n5.nabble.com/Recursion-with-Macro-tp5723537p5723547.html
Sent from the SPSSX Discussion mailing list archive at Nabble.com.

=====================
To manage your subscription to SPSSX-L, send a message to [hidden email] (not to SPSSX-L), with no body text except the command. To leave the list, send the command SIGNOFF SPSSX-L For a list of commands to manage subscriptions, send the command INFO REFCARD

=====================
To manage your subscription to SPSSX-L, send a message to
[hidden email] (not to SPSSX-L), with no body text except the
command. To leave the list, send the command
SIGNOFF SPSSX-L
For a list of commands to manage subscriptions, send the command
INFO REFCARD
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

David Marso
Administrator
Sure Gene,
You lit the fire ;-)
It is interesting to compare the RECURSIVE version to an ITERATIVE one.
They both punch out the same code but somehow the RECURSE macro seems much tidier/elegant than the ITERATE macro

DEFINE RECURSE (!POS !TOKENS(1) /!POS !TOKENS(1) / !POS !CMDEND).
!IF (!TAIL(!3) !NE !NULL) !THEN
!1 !HEAD(!3) !2 !TAIL(!3) .
RECURSE !1 !2 !TAIL(!3) .
!IFEND
!ENDDEFINE.
RECURSE CROSSTABS BY A B C D E F G H I J K L M.


DEFINE ITERATE (!POS !TOKENS(1) /!POS !TOKENS(1) / !POS !CMDEND).
!LET !Cpy=!3
!DO !H !IN (!TAIL(!3))
!1 !HEAD(!Cpy) !2 !TAIL(!Cpy).
!LET !Cpy=!TAIL(!Cpy)
!DOEND  
!ENDDEFINE.
ITERATE CROSSTABS BY A B C D E F G H I J K L M.

Maguin, Eugene wrote
Bruce, thanks for posting the expansion.
David, thanks for imagining and illustrating what is possible.
Gene Maguin

-----Original Message-----
From: SPSSX(r) Discussion [mailto:[hidden email]] On Behalf Of Bruce Weaver
Sent: Monday, December 09, 2013 4:40 PM
To: [hidden email]
Subject: Re: Recursion with Macro ;-)

For the benefit of those who don't have a current SPSS license (or who just have not bothered to run the syntax), here is what David's magical macro generates (obtained by setting MPRINT ON before running the macros):

SET MPRINT ON.
RECURSE CROSSTABS BY A B C D E F G H I J K L M.
2144  0 M>
2145  0 M>  .
2146  0 M>  CROSSTABS A BY B C D E F G H I J K L M.
2147  0 M>
2148  0 M>  .
2149  0 M>  CROSSTABS B BY C D E F G H I J K L M.
2150  0 M>
2151  0 M>  .
2152  0 M>  CROSSTABS C BY D E F G H I J K L M.
2153  0 M>
2154  0 M>  .
2155  0 M>  CROSSTABS D BY E F G H I J K L M.
2156  0 M>
2157  0 M>  .
2158  0 M>  CROSSTABS E BY F G H I J K L M.
2159  0 M>
2160  0 M>  .
2161  0 M>  CROSSTABS F BY G H I J K L M.
2162  0 M>
2163  0 M>  .
2164  0 M>  CROSSTABS G BY H I J K L M.
2165  0 M>
2166  0 M>  .
2167  0 M>  CROSSTABS H BY I J K L M.
2168  0 M>
2169  0 M>  .
2170  0 M>  CROSSTABS I BY J K L M.
2171  0 M>
2172  0 M>  .
2173  0 M>  CROSSTABS J BY K L M.
2174  0 M>
2175  0 M>  .
2176  0 M>  CROSSTABS K BY L M.
2177  0 M>
2178  0 M>  .
2179  0 M>  CROSSTABS L BY M.
2180  0 M>

RECURSE CORRELATION WITH A B C D E F G H I J K L M.

2196  0 M>
2197  0 M>  .
2198  0 M>  CORRELATION A WITH B C D E F G H I J K L M.
2199  0 M>
2200  0 M>  .
2201  0 M>  CORRELATION B WITH C D E F G H I J K L M.
2202  0 M>
2203  0 M>  .
2204  0 M>  CORRELATION C WITH D E F G H I J K L M.
2205  0 M>
2206  0 M>  .
2207  0 M>  CORRELATION D WITH E F G H I J K L M.
2208  0 M>
2209  0 M>  .
2210  0 M>  CORRELATION E WITH F G H I J K L M.
2211  0 M>
2212  0 M>  .
2213  0 M>  CORRELATION F WITH G H I J K L M.
2214  0 M>
2215  0 M>  .
2216  0 M>  CORRELATION G WITH H I J K L M.
2217  0 M>
2218  0 M>  .
2219  0 M>  CORRELATION H WITH I J K L M.
2220  0 M>
2221  0 M>  .
2222  0 M>  CORRELATION I WITH J K L M.
2223  0 M>
2224  0 M>  .
2225  0 M>  CORRELATION J WITH K L M.
2226  0 M>
2227  0 M>  .
2228  0 M>  CORRELATION K WITH L M.
2229  0 M>
2230  0 M>  .
2231  0 M>  CORRELATION L WITH M.
2232  0 M>

SET MPRINT OFF.
2248  0 M>  SET MPRINT OFF.


That's pretty slick.  ;-)



David Marso wrote
> OK Bruce,
> I don't want to see any green junk flying after your head stops
> spinning
> ;-)))
> LOL
>
> DEFINE RECURSE (!POS !TOKENS(1) /!POS !TOKENS(1) / !POS !CMDEND).
> !IF (!TAIL(!3) !NE !NULL) !THEN
> !1 !HEAD(!3) !2 !TAIL(!3) .
> RECURSE  !1 !2 !TAIL(!3) .
> !IFEND
> !ENDDEFINE.
>
> SET MNEST 100.
> DATA LIST FREE /A B C D E F G H I J K L M .
> BEGIN DATA
> 1 1 5 3 1 3 6 1 5 3 1 5 3
> 6 2 6 1 5 3 2 5 3 6 5 2 3
> 5 1 2 5 6 3 5 1 5 3 6 2 5
> 3 2 1 5 3 6 1 2 5 3 1 2 5
> 3 5 6 1 2 5 6 3 7 3 5 1 2
> 4 5 6 3 2 6 7 5 3 1 2 3 4
> END DATA.
> RECURSE CROSSTABS BY A B C D E F G H I J K L M.
> RECURSE CORRELATION WITH A B C D E F G H I J K L M.





-----
--
Bruce Weaver
[hidden email]
http://sites.google.com/a/lakeheadu.ca/bweaver/

"When all else fails, RTFM."

NOTE: My Hotmail account is not monitored regularly.
To send me an e-mail, please use the address shown above.

--
View this message in context: http://spssx-discussion.1045642.n5.nabble.com/Recursion-with-Macro-tp5723537p5723547.html
Sent from the SPSSX Discussion mailing list archive at Nabble.com.

=====================
To manage your subscription to SPSSX-L, send a message to [hidden email] (not to SPSSX-L), with no body text except the command. To leave the list, send the command SIGNOFF SPSSX-L For a list of commands to manage subscriptions, send the command INFO REFCARD

=====================
To manage your subscription to SPSSX-L, send a message to
[hidden email] (not to SPSSX-L), with no body text except the
command. To leave the list, send the command
SIGNOFF SPSSX-L
For a list of commands to manage subscriptions, send the command
INFO REFCARD
Please reply to the list and not to my personal email.
Those desiring my consulting or training services please feel free to email me.
---
"Nolite dare sanctum canibus neque mittatis margaritas vestras ante porcos ne forte conculcent eas pedibus suis."
Cum es damnatorum possederunt porcos iens ut salire off sanguinum cliff in abyssum?"
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

David Marso
Administrator
More fun.
Passing an arbitrary macro name as a parameter to a macro which recursively parses two lists in parallel and invokes the macro with the appropriate set of paired values from the lists ;-)
Note that DoSomething and DoSomethingElse could be arbitrarily complex (include OMS, complex data smashing etc).
Bzzzt!

DEFINE DoSomething (!POS !CMDEND).
CORRELATION !HEAD(!1) WITH !TAIL(!1) .
!ENDDEFINE .

DEFINE DoSomethingElse (!POS !CMDEND).
ONEWAY !HEAD(!1) BY !TAIL(!1) .
!ENDDEFINE .

DEFINE Parallel
  (!POS !CHAREND ("/") / !POS !CHAREND ("/") / !POS !CMDEND ).
!UNQUOTE(!1) !HEAD(!2) !HEAD(!3) .
!IF (!TAIL(!2) !NE !NULL) !THEN  
Parallel !1 / !TAIL(!2) / !TAIL(!3).
!IFEND
!ENDDEFINE.

DATA LIST FREE / a b c d e f.
BEGIN DATA
1 2 3 2 3 4
5 6 7 8 9 1
2 3 4 5 1 2
3 3 5 6 7 8
END DATA.
Parallel 'DoSomething' /a b c /d e f.
Parallel 'DoSomethingElse' /a b c /d e f.
Please reply to the list and not to my personal email.
Those desiring my consulting or training services please feel free to email me.
---
"Nolite dare sanctum canibus neque mittatis margaritas vestras ante porcos ne forte conculcent eas pedibus suis."
Cum es damnatorum possederunt porcos iens ut salire off sanguinum cliff in abyssum?"
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

Richard Ristow
At 11:51 AM 12/11/2013, David Marso wrote:

>More fun: Passing an arbitrary macro name as a parameter to a macro
>which recursively parses two lists in parallel and invokes the macro
>with the appropriate set of paired values from the lists ;-)

[Definitions of the argument macros omitted]

>DEFINE Parallel
>   (!POS !CHAREND ("/") / !POS !CHAREND ("/") / !POS !CMDEND ).
>!UNQUOTE(!1) !HEAD(!2) !HEAD(!3) .
>!IF (!TAIL(!2) !NE !NULL) !THEN
>Parallel !1 / !TAIL(!2) / !TAIL(!3).
>!IFEND
>!ENDDEFINE.

That's a nice job, and a nice way to step in parallel through two
lists. (Direct implementation using "!DO !varname !IN (list)" is
awkward: !DO, unlike DO REPEAT, which it resembles, cannot directly
step through multiple lists in parallel, requiring !HEAD/!TAIL code
to handle all but the first list.)

In computer-science terminology, your method here is not true
recursion, but what is called tail-recursion: A program calls itself,
but only as its last act before terminating. It's fundamentally
easier to implement than is true recursion, because it doesn't
require that the first instance's local data be preserved separate
from the second instance's version of the same data. Tail-recursion
can always be replaced by a loop, unlike, say,

>Fib(n)=Fib(n-1)+Fib(n-2)
>Fib(1)=Fib(2)=1

I don't think true recursion is possible in SPSS macros, because they
have no local storage -- I've looked, and I think all your examples
are effectively tail-recursion. But that's a nice addition to the
armory of SPSS macro-coding techniques.

=====================
To manage your subscription to SPSSX-L, send a message to
[hidden email] (not to SPSSX-L), with no body text except the
command. To leave the list, send the command
SIGNOFF SPSSX-L
For a list of commands to manage subscriptions, send the command
INFO REFCARD
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

David Marso
Administrator
"I don't think true recursion is possible in SPSS macros, because they
have no local storage -- I've looked, and I think all your examples
are effectively tail-recursion. But that's a nice addition to the
armory of SPSS macro-coding techniques. "

Thanks for your kind words Richard.
Does the very first example at the beginning of the thread count?
I am unclear as to what the macro has as far as storage in something like this.
Notice the 'ascii art' starts LONG and shrinks as successive calls 'pop' off the stack.
Notice without MNEST set adequately it will fail with a stack error.
I am not versed in comp sci so would not be the person to ask.
OTOH:  I believe this to be a neat development in my own incurable macro-philia.
Just when I thought there was nothing left to play with in macro I get bit on the brain by this thing ;-)


Richard Ristow wrote
At 11:51 AM 12/11/2013, David Marso wrote:

>More fun: Passing an arbitrary macro name as a parameter to a macro
>which recursively parses two lists in parallel and invokes the macro
>with the appropriate set of paired values from the lists ;-)

[Definitions of the argument macros omitted]

>DEFINE Parallel
>   (!POS !CHAREND ("/") / !POS !CHAREND ("/") / !POS !CMDEND ).
>!UNQUOTE(!1) !HEAD(!2) !HEAD(!3) .
>!IF (!TAIL(!2) !NE !NULL) !THEN
>Parallel !1 / !TAIL(!2) / !TAIL(!3).
>!IFEND
>!ENDDEFINE.

That's a nice job, and a nice way to step in parallel through two
lists. (Direct implementation using "!DO !varname !IN (list)" is
awkward: !DO, unlike DO REPEAT, which it resembles, cannot directly
step through multiple lists in parallel, requiring !HEAD/!TAIL code
to handle all but the first list.)

In computer-science terminology, your method here is not true
recursion, but what is called tail-recursion: A program calls itself,
but only as its last act before terminating. It's fundamentally
easier to implement than is true recursion, because it doesn't
require that the first instance's local data be preserved separate
from the second instance's version of the same data. Tail-recursion
can always be replaced by a loop, unlike, say,

>Fib(n)=Fib(n-1)+Fib(n-2)
>Fib(1)=Fib(2)=1

I don't think true recursion is possible in SPSS macros, because they
have no local storage -- I've looked, and I think all your examples
are effectively tail-recursion. But that's a nice addition to the
armory of SPSS macro-coding techniques.

=====================
To manage your subscription to SPSSX-L, send a message to
[hidden email] (not to SPSSX-L), with no body text except the
command. To leave the list, send the command
SIGNOFF SPSSX-L
For a list of commands to manage subscriptions, send the command
INFO REFCARD
Please reply to the list and not to my personal email.
Those desiring my consulting or training services please feel free to email me.
---
"Nolite dare sanctum canibus neque mittatis margaritas vestras ante porcos ne forte conculcent eas pedibus suis."
Cum es damnatorum possederunt porcos iens ut salire off sanguinum cliff in abyssum?"
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

David Marso
Administrator
The more I reflect the more I am convinced that when the macro invokes the recursive call it is the same as if it were calling a completely different macro (i.e. all existing internal values are preserved until the return).  How could it be otherwise?  After all, it's not as though they have self awareness or anything ;-)
So all the goodies are on the stack until the whole thing unwinds.

David Marso wrote
"I don't think true recursion is possible in SPSS macros, because they
have no local storage -- I've looked, and I think all your examples
are effectively tail-recursion. But that's a nice addition to the
armory of SPSS macro-coding techniques. "

Thanks for your kind words Richard.
Does the very first example at the beginning of the thread count?
I am unclear as to what the macro has as far as storage in something like this.
Notice the 'ascii art' starts LONG and shrinks as successive calls 'pop' off the stack.
Notice without MNEST set adequately it will fail with a stack error.
I am not versed in comp sci so would not be the person to ask.
OTOH:  I believe this to be a neat development in my own incurable macro-philia.
Just when I thought there was nothing left to play with in macro I get bit on the brain by this thing ;-)


Richard Ristow wrote
At 11:51 AM 12/11/2013, David Marso wrote:

>More fun: Passing an arbitrary macro name as a parameter to a macro
>which recursively parses two lists in parallel and invokes the macro
>with the appropriate set of paired values from the lists ;-)

[Definitions of the argument macros omitted]

>DEFINE Parallel
>   (!POS !CHAREND ("/") / !POS !CHAREND ("/") / !POS !CMDEND ).
>!UNQUOTE(!1) !HEAD(!2) !HEAD(!3) .
>!IF (!TAIL(!2) !NE !NULL) !THEN
>Parallel !1 / !TAIL(!2) / !TAIL(!3).
>!IFEND
>!ENDDEFINE.

That's a nice job, and a nice way to step in parallel through two
lists. (Direct implementation using "!DO !varname !IN (list)" is
awkward: !DO, unlike DO REPEAT, which it resembles, cannot directly
step through multiple lists in parallel, requiring !HEAD/!TAIL code
to handle all but the first list.)

In computer-science terminology, your method here is not true
recursion, but what is called tail-recursion: A program calls itself,
but only as its last act before terminating. It's fundamentally
easier to implement than is true recursion, because it doesn't
require that the first instance's local data be preserved separate
from the second instance's version of the same data. Tail-recursion
can always be replaced by a loop, unlike, say,

>Fib(n)=Fib(n-1)+Fib(n-2)
>Fib(1)=Fib(2)=1

I don't think true recursion is possible in SPSS macros, because they
have no local storage -- I've looked, and I think all your examples
are effectively tail-recursion. But that's a nice addition to the
armory of SPSS macro-coding techniques.

=====================
To manage your subscription to SPSSX-L, send a message to
[hidden email] (not to SPSSX-L), with no body text except the
command. To leave the list, send the command
SIGNOFF SPSSX-L
For a list of commands to manage subscriptions, send the command
INFO REFCARD
Please reply to the list and not to my personal email.
Those desiring my consulting or training services please feel free to email me.
---
"Nolite dare sanctum canibus neque mittatis margaritas vestras ante porcos ne forte conculcent eas pedibus suis."
Cum es damnatorum possederunt porcos iens ut salire off sanguinum cliff in abyssum?"
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

David Marso
Administrator
In reply to this post by Richard Ristow
Here is an incredibly inefficient 'recursive?' MACRO implementation of Richard's Fib (Fibonacci sequence for those scratching their heads). 
Of course one should NEVER actually use this code. 
It is merely an example of another macro which plays with itself ;-)
This is akin to gluing together millions/billions of dung-bricks using spit and dirt and then measuring the result by pacing toe to foot while using a walker with one broken arm (while drunk ) while being pursued by an angry nest of army ants and a swarm of Africanized bees.
note that Fib 47 results in a final chunk of dung almost 3 billion units long
-The fact that SPSS can assemble a string that large ?quickly? is most impressive-.
Too bad nobody ever taught macro basic math

* n1 n2 fibn it X * .

* n1 n2 fibn it X * .
DEFINE FibHelper
  (!POS !TOKENS(1)/!POS !TOKENS(1)/!POS !TOKENS(1)/!POS !TOKENS(1)).
!LET !IT    = !LENGTH(!CONCAT(!BLANKS(1 ),!BLANKS(!3)))
!LET !FibN  = !LENGTH(!CONCAT(!BLANKS(!1),!BLANKS(!2)))
!IF (!IT !NE !4) !THEN
fibhelper !2 !FibN !IT !4
!ELSE
ECHO !QUOTE(!FibN) .
!IFEND
!ENDDEFINE.

DEFINE fib (!POS !TOKENS(1)).
PRESERVE.
SET MNEST=!1.
FibHelper 0 1 1 !1
RESTORE.
!ENDDEFINE .
SET MPRINT OFF .
Fib 47.
Fib 47.
2,971,215,073


DEFINE Fib_Matrix (!POS !TOKENS(1)).
PRESERVE.
SET MXLOOPS !1 .
MATRIX.
COMPUTE FibX=MAKE(!1,1,1).
LOOP #=3 TO !1.
COMPUTE FibX(#)=FibX(#-2)+FibX(#-1).
END LOOP.
PRINT FibX(!1) /FORMAT "F40.0".
END MATRIX.
RESTORE.
!ENDDEFINE.
Fib_Matrix 180.




On Wed, Dec 11, 2013 at 3:21 PM, Richard Ristow [via SPSSX Discussion] <[hidden email]> wrote:
At 11:51 AM 12/11/2013, David Marso wrote:

>More fun: Passing an arbitrary macro name as a parameter to a macro
>which recursively parses two lists in parallel and invokes the macro
>with the appropriate set of paired values from the lists ;-)

[Definitions of the argument macros omitted]

>DEFINE Parallel
>   (!POS !CHAREND ("/") / !POS !CHAREND ("/") / !POS !CMDEND ).
>!UNQUOTE(!1) !HEAD(!2) !HEAD(!3) .
>!IF (!TAIL(!2) !NE !NULL) !THEN
>Parallel !1 / !TAIL(!2) / !TAIL(!3).
>!IFEND
>!ENDDEFINE.

That's a nice job, and a nice way to step in parallel through two
lists. (Direct implementation using "!DO !varname !IN (list)" is
awkward: !DO, unlike DO REPEAT, which it resembles, cannot directly
step through multiple lists in parallel, requiring !HEAD/!TAIL code
to handle all but the first list.)

In computer-science terminology, your method here is not true
recursion, but what is called tail-recursion: A program calls itself,
but only as its last act before terminating. It's fundamentally
easier to implement than is true recursion, because it doesn't
require that the first instance's local data be preserved separate
from the second instance's version of the same data. Tail-recursion
can always be replaced by a loop, unlike, say,

>Fib(n)=Fib(n-1)+Fib(n-2)
>Fib(1)=Fib(2)=1

I don't think true recursion is possible in SPSS macros, because they
have no local storage -- I've looked, and I think all your examples
are effectively tail-recursion. But that's a nice addition to the
armory of SPSS macro-coding techniques.

=====================
To manage your subscription to SPSSX-L, send a message to
[hidden email] (not to SPSSX-L), with no body text except the
command. To leave the list, send the command
SIGNOFF SPSSX-L
For a list of commands to manage subscriptions, send the command
INFO REFCARD



If you reply to this email, your message will be added to the discussion below:
http://spssx-discussion.1045642.n5.nabble.com/Recursion-with-Macro-tp5723537p5723611.html
To unsubscribe from Recursion with Macro ;-), click here.
NAML

Please reply to the list and not to my personal email.
Those desiring my consulting or training services please feel free to email me.
---
"Nolite dare sanctum canibus neque mittatis margaritas vestras ante porcos ne forte conculcent eas pedibus suis."
Cum es damnatorum possederunt porcos iens ut salire off sanguinum cliff in abyssum?"
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

Richard Ristow
In reply to this post by David Marso
At 03:57 AM 12/12/2013, David Marso wrote:

>The more I reflect the more I am convinced that when the macro
>invokes the recursive call it is the same as if it were calling a
>completely different macro (i.e. all existing internal values are
>preserved until the return). How could it be otherwise?

It could be otherwise very easily, and is in many programming systems.

Some programming environments have a single 'name space'. In those, a
name (say, "A") always refers to the same entity, no matter where in
the code it appears; no values are 'internal' to a procedure. And in
many others, names are local to procedures but not to instances of
those procedures. In both cases, if a procedure calls itself, the
called instance may change the caller's internal values.

The SPSS macro environment couldn't well follow the first of those
models, since macro variables can't exist except within a macro; but
I'd assumed it would follow the second. Interestingly (and
undocumentedly) it does not, but binds variables to each *instance*
of a macro invocation; see David's examples, or Example II, below.

SPSS macros apparently can't 'see' variables belonging to macros that
call them, even when they have no variables with the same name; see
both examples. (Thus, until David Marso thinks of a way -- how's that
for confidence? -- a macro can't return values to its caller. So,
alas, a macro like "Plus", to take two arguments and return their
sum, appears impossible.)

One very strange quirk: If a macro calls a *different* macro,
variables named in the calling macro are undefined (until assigned)
in the macro called; but if a macro calls *itself*, variables named
in the caller are defined, but blank, in the called instance: compare
the values of !B in the called macros, in the two examples.

*  Test of preservation of macro-variable values across calls:        .

*  Example I:  A macro calls a different macro:                       .

DEFINE !Outer()
    ECHO  'Starting macro "<!>Outer":'.
    !LET !A = 'Alpha'
    !LET !B = 'Beta'
    ECHO !QUOTE(!CONCAT('A     = ',!A)).
    ECHO !QUOTE(!CONCAT('B     = ',!B)).
    !Inner

    ECHO 'Back from the call to "<!>Inner":',
    ECHO !QUOTE(!CONCAT('Depth = ',!Depth)).
    ECHO !QUOTE(!CONCAT('A     = ',!A)).
    ECHO !QUOTE(!CONCAT('B     = ',!B)).
!ENDDEFINE.

DEFINE !Inner()
    !LET !A = 'Gamma'
    ECHO 'In the macro "<!>Inner":'.
    ECHO !QUOTE(!CONCAT('A     = ',!A)).
    ECHO !QUOTE(!CONCAT('B     = ',!B)).
!ENDDEFINE.

!Outer.
Starting macro "<!>Outer":
A     = Alpha
B     = Beta
In the macro "<!>Inner":
A     = Gamma
B     = !B
Back from the call to "<!>Inner":
A     = Alpha
B     = Beta

*  Example II: A macro calls itself (i.e., a recursive call):         .

DEFINE !Cursive (Depth=!TOKENS(1))
  !IF (!Depth !LT 2) !THEN
    !LET !A = 'Alpha'
    !LET !B = 'Beta'
    ECHO !QUOTE(!CONCAT('Depth = ',!Depth)).
    ECHO !QUOTE(!CONCAT('A     = ',!A)).
    ECHO !QUOTE(!CONCAT('B     = ',!B)).
    !Cursive Depth = 2
    ECHO 'Back from the recursive call:',
    ECHO !QUOTE(!CONCAT('Depth = ',!Depth)).
    ECHO !QUOTE(!CONCAT('A     = ',!A)).
    ECHO !QUOTE(!CONCAT('B     = ',!B)).
  !IFEND
  !IF (!Depth !GE 2) !THEN
    !LET !A = 'Gamma'
    ECHO 'In the recursive instance:'.
    ECHO !QUOTE(!CONCAT('Depth = ',!Depth)).
    ECHO !QUOTE(!CONCAT('A     = ',!A)).
    ECHO !QUOTE(!CONCAT('B     = ',!B)).
  !IFEND
!ENDDEFINE.

!Cursive Depth=1.
Depth = 1
A     = Alpha
B     = Beta
In the recursive instance:
Depth = 2
A     = Gamma
B     =
Back from the recursive call:
A     = Alpha
B     = Beta
===================
APPENDIX: Test code
===================
*  C:\Documents and Settings\Richard\My Documents   .
*    \Technical\spssx-l\Z-2013\                     .
*    2013-12-12 Marso-Recursion with Macro-B.SPS    .

*  Date:     Thu, 12 Dec 2013 00:57:37 -0800        .
*  From:     David Marso <[hidden email]>    .
*  Subject:  Re: Recursion with Macro ;-)           .
*  To:       [hidden email]               .

*  (Note that there are other postings by the same author, in the     .
*  same thread)                                                       .

*  "The more I reflect the more I am convinced that when the macro    .
*  invokes the recursive call it is the same as if it were calling a  .
*  completely different macro (i.e. all existing internal values are  .
*  preserved until the return). How could it be otherwise?"           .

*  Test of preservation of macro-variable values across calls:        .

*  Example I:  A macro calls a different macro:                       .

DEFINE !Outer()
    ECHO  'Starting macro "<!>Outer":'.
    !LET !A = 'Alpha'
    !LET !B = 'Beta'
    ECHO !QUOTE(!CONCAT('A     = ',!A)).
    ECHO !QUOTE(!CONCAT('B     = ',!B)).
    !Inner

    ECHO 'Back from the call to "<!>Inner":',
    ECHO !QUOTE(!CONCAT('Depth = ',!Depth)).
    ECHO !QUOTE(!CONCAT('A     = ',!A)).
    ECHO !QUOTE(!CONCAT('B     = ',!B)).
!ENDDEFINE.

DEFINE !Inner()
    !LET !A = 'Gamma'
    ECHO 'In the macro "<!>Inner":'.
    ECHO !QUOTE(!CONCAT('A     = ',!A)).
    ECHO !QUOTE(!CONCAT('B     = ',!B)).
!ENDDEFINE.

!Outer.

*  Example II: A macro calls itself (i.e., a recursive call):         .

DEFINE !Cursive (Depth=!TOKENS(1))
  !IF (!Depth !LT 2) !THEN
    !LET !A = 'Alpha'
    !LET !B = 'Beta'
    ECHO !QUOTE(!CONCAT('Depth = ',!Depth)).
    ECHO !QUOTE(!CONCAT('A     = ',!A)).
    ECHO !QUOTE(!CONCAT('B     = ',!B)).
    !Cursive Depth = 2
    ECHO 'Back from the recursive call:',
    ECHO !QUOTE(!CONCAT('Depth = ',!Depth)).
    ECHO !QUOTE(!CONCAT('A     = ',!A)).
    ECHO !QUOTE(!CONCAT('B     = ',!B)).
  !IFEND
  !IF (!Depth !GE 2) !THEN
    !LET !A = 'Gamma'
    ECHO 'In the recursive instance:'.
    ECHO !QUOTE(!CONCAT('Depth = ',!Depth)).
    ECHO !QUOTE(!CONCAT('A     = ',!A)).
    ECHO !QUOTE(!CONCAT('B     = ',!B)).
  !IFEND
!ENDDEFINE.

!Cursive Depth=1.

=====================
To manage your subscription to SPSSX-L, send a message to
[hidden email] (not to SPSSX-L), with no body text except the
command. To leave the list, send the command
SIGNOFF SPSSX-L
For a list of commands to manage subscriptions, send the command
INFO REFCARD
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

Richard Ristow
In reply to this post by David Marso
At 11:51 AM 12/11/2013, David Marso wrote:

As David Marso has demonstrated, SPSS macros can carry out
tail-recursion very neatly. (To recapitulate: a program is
tail-recursive if it calls itself as its last act before terminating.)

Tail recursion is inherently simpler than full recursion; it can
always be replaced by a loop. However, as David Marso has worked out,
tail-recursion can be a neater way than explicit looping for writing
macros to get certain effects: particularly, for acting on all tuples
of corresponding elements of a set of lists; and for acting on all
pairs of elements of a single list. Both are illustrated below, using
ECHO to print the tuples (in the first example) and the pairs (in the second).

*  I.   Step through three lists in parallel. This may be modified    .
*       for more or fewer lists; and the action taken on each triple  .
*       may be anything desired.                                      .
*       (Code adapted from David Marso.)                              .

DEFINE !Parallel3
   ( !POS !CHAREND ("/")
   / !POS !CHAREND ("/")
   / !POS !CMDEND ).

   !LET !Msg = !CONCAT('  ',
                       !HEAD(!1),' ',
                       !HEAD(!2),' ',
                       !HEAD(!3))

.  ECHO !QUOTE(!Msg).

    !IF (!TAIL(!1) !NE !NULL) !THEN
        !Parallel3 !TAIL(!1) / !TAIL(!2) / !TAIL(!3).
    !IFEND
!ENDDEFINE.

!Parallel3   1 2 3
            / A B C
            / Alpha Beta Gamma.
   1 A Alpha
   2 B Beta
   3 C Gamma


*  II.  Generate all pairs of two elements from a list. Again, the    .
*       the action taken on each pair may be anything desired.        .
*       (For clarity, this macro emits a blank line before the output .
*       from each invocation.)                                        .

DEFINE !AllPairs (!POS !CMDEND ).

.  ECHO ' '.

    !LET !Left   =   !HEAD(!1)
    !DO  !Right !IN (!TAIL(!1))
       !LET !Msg = !CONCAT('  ',!Left,' ',!Right)
.     ECHO !QUOTE(!Msg).
    !DOEND

    !IF (!TAIL(!TAIL(!1)) !NE !NULL) !THEN
      !AllPairs !TAIL(!1) .
    !IFEND

!ENDDEFINE.


!AllPairs A B C D E.

   A B
   A C
   A D
   A E

   B C
   B D
   B E

   C D
   C E

   D E

!AllPairs A.

==================
APPENDIX: All code
==================
*  C:\Documents and Settings\Richard\My Documents  .
*    \Technical\spssx-l\Z-2013\                    .
*    2013-12-11 Marso-Recursion with Macro-C.SPS   .

*  In response to posting                          .
*  Date:     Wed, 11 Dec 2013 08:51:32 -0800       .
*  From:     David Marso <[hidden email]>   .
*  Subject:  Re: Recursion with Macro ;-)          .
*  To:       [hidden email]              .

*  Note: There are other postings by this author in this thread. And  .
*  this is the third example code I've written for posting in this    .
*  thread. (The other two are named with date 2013-12-12.)            .

*  "Passing an arbitrary macro name as a parameter to a macro which   .
*  recursively parses two lists in parallel and invokes the macro     .
*  with the appropriate set of paired values from the lists ;-)"      .

*  This code has illustrations using tail-recursion to step through   .
*  three lists in parallel (deliberately simplified from David's);    .
*  and to emit all possible pairs from two lists.                     .


*  I.   Step through three lists in parallel. This may be modified    .
*       for more or fewer lists; and the action taken on each triple  .
*       may be anything desired.                                      .
*       (Code adapted from David Marso.)                              .

DEFINE !Parallel3
   ( !POS !CHAREND ("/")
   / !POS !CHAREND ("/")
   / !POS !CMDEND ).

   !LET !Msg = !CONCAT('  ',
                       !HEAD(!1),' ',
                       !HEAD(!2),' ',
                       !HEAD(!3))

.  ECHO !QUOTE(!Msg).

    !IF (!TAIL(!1) !NE !NULL) !THEN
        !Parallel3 !TAIL(!1) / !TAIL(!2) / !TAIL(!3).
    !IFEND
!ENDDEFINE.

!Parallel3   1 2 3
            / A B C
            / Alpha Beta Gamma.


*  II.  Generate all pairs of two elements from a list. Again, the    .
*       the action taken on each pair may be anything desired.        .
*       (For clarity, this macro emits a blank line before the output .
*       from each invocation.)                                        .

DEFINE !AllPairs (!POS !CMDEND ).

.  ECHO ' '.

    !LET !Left   =   !HEAD(!1)
    !DO  !Right !IN (!TAIL(!1))
       !LET !Msg = !CONCAT('  ',!Left,' ',!Right)
.     ECHO !QUOTE(!Msg).
    !DOEND

    !IF (!TAIL(!TAIL(!1)) !NE !NULL) !THEN
      !AllPairs !TAIL(!1) .
    !IFEND

!ENDDEFINE.


!AllPairs A B C D E.

!AllPairs A.

=====================
To manage your subscription to SPSSX-L, send a message to
[hidden email] (not to SPSSX-L), with no body text except the
command. To leave the list, send the command
SIGNOFF SPSSX-L
For a list of commands to manage subscriptions, send the command
INFO REFCARD
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

David Marso
Administrator
Thanks Richard,
I like these examples.
I have taken the !AllPairs to the next logical place and created !AllComb;-)
If one wants more than 255 chars then output the result to a file!
Can you provide an example of a recursive function which is not tail recursion?
There has to be something more than simply the recursive call being the LAST thing in the 'function'.
If I were to place an ECHO command after the call to !AllComb would that be enough to make this no longer Tail recursion (not that it matters, I like this 'new' technique and really don't care what technical labels anyone wants to place on it)?

DEFINE !AllComb (!POS !CHAREND ("/") /!POS !CMDEND).
!LET !R=""
!DO !I !IN (!2)
  !LET !I2=!SUBSTR(!I,!LENGTH(!I),1).
    !DO !J !IN (!1)
      !IF (!INDEX(!I,!J) !EQ 0 !AND !J !GT !I2)  !THEN
        !LET !R=!CONCAT(!R,' ',!UNQUOTE(!I),!J)
      !IFEND  
    !DOEND
!DOEND
ECHO "-".
ECHO !QUOTE(!R).
!IF (!TAIL(!1) !NE !NULL) !THEN
!AllComb !TAIL(!1) / !R .
!IFEND
!ENDDEFINE .
!AllComb A B C D E F  / '' .

 A B C D E F
-
 AB AC AD AE AF BC BD BE BF CD CE CF DE DF EF
-
 ABC ABD ABE ABF ACD ACE ACF ADE ADF AEF BCD BCE BCF BDE BDF BEF CDE CDF CEF DEF
-
 ABCD ABCE ABCF ABDE ABDF ABEF ACDE ACDF ACEF ADEF BCDE BCDF BCEF BDEF CDEF
-
 ABCDE ABCDF ABCEF ABDEF ACDEF BCDEF
-
 ABCDEF

Richard Ristow wrote
At 11:51 AM 12/11/2013, David Marso wrote:

As David Marso has demonstrated, SPSS macros can carry out
tail-recursion very neatly. (To recapitulate: a program is
tail-recursive if it calls itself as its last act before terminating.)

Tail recursion is inherently simpler than full recursion; it can
always be replaced by a loop. However, as David Marso has worked out,
tail-recursion can be a neater way than explicit looping for writing
macros to get certain effects: particularly, for acting on all tuples
of corresponding elements of a set of lists; and for acting on all
pairs of elements of a single list. Both are illustrated below, using
ECHO to print the tuples (in the first example) and the pairs (in the second).

*  I.   Step through three lists in parallel. This may be modified    .
*       for more or fewer lists; and the action taken on each triple  .
*       may be anything desired.                                      .
*       (Code adapted from David Marso.)                              .

DEFINE !Parallel3
   ( !POS !CHAREND ("/")
   / !POS !CHAREND ("/")
   / !POS !CMDEND ).

   !LET !Msg = !CONCAT('  ',
                       !HEAD(!1),' ',
                       !HEAD(!2),' ',
                       !HEAD(!3))

.  ECHO !QUOTE(!Msg).

    !IF (!TAIL(!1) !NE !NULL) !THEN
        !Parallel3 !TAIL(!1) / !TAIL(!2) / !TAIL(!3).
    !IFEND
!ENDDEFINE.

!Parallel3   1 2 3
            / A B C
            / Alpha Beta Gamma.
   1 A Alpha
   2 B Beta
   3 C Gamma


*  II.  Generate all pairs of two elements from a list. Again, the    .
*       the action taken on each pair may be anything desired.        .
*       (For clarity, this macro emits a blank line before the output .
*       from each invocation.)                                        .

DEFINE !AllPairs (!POS !CMDEND ).

.  ECHO ' '.

    !LET !Left   =   !HEAD(!1)
    !DO  !Right !IN (!TAIL(!1))
       !LET !Msg = !CONCAT('  ',!Left,' ',!Right)
.     ECHO !QUOTE(!Msg).
    !DOEND

    !IF (!TAIL(!TAIL(!1)) !NE !NULL) !THEN
      !AllPairs !TAIL(!1) .
    !IFEND

!ENDDEFINE.


!AllPairs A B C D E.

   A B
   A C
   A D
   A E

   B C
   B D
   B E

   C D
   C E

   D E

!AllPairs A.

==================
APPENDIX: All code
==================
*  C:\Documents and Settings\Richard\My Documents  .
*    \Technical\spssx-l\Z-2013\                    .
*    2013-12-11 Marso-Recursion with Macro-C.SPS   .

*  In response to posting                          .
*  Date:     Wed, 11 Dec 2013 08:51:32 -0800       .
*  From:     David Marso <[hidden email]>   .
*  Subject:  Re: Recursion with Macro ;-)          .
*  To:       [hidden email]              .

*  Note: There are other postings by this author in this thread. And  .
*  this is the third example code I've written for posting in this    .
*  thread. (The other two are named with date 2013-12-12.)            .

*  "Passing an arbitrary macro name as a parameter to a macro which   .
*  recursively parses two lists in parallel and invokes the macro     .
*  with the appropriate set of paired values from the lists ;-)"      .

*  This code has illustrations using tail-recursion to step through   .
*  three lists in parallel (deliberately simplified from David's);    .
*  and to emit all possible pairs from two lists.                     .


*  I.   Step through three lists in parallel. This may be modified    .
*       for more or fewer lists; and the action taken on each triple  .
*       may be anything desired.                                      .
*       (Code adapted from David Marso.)                              .

DEFINE !Parallel3
   ( !POS !CHAREND ("/")
   / !POS !CHAREND ("/")
   / !POS !CMDEND ).

   !LET !Msg = !CONCAT('  ',
                       !HEAD(!1),' ',
                       !HEAD(!2),' ',
                       !HEAD(!3))

.  ECHO !QUOTE(!Msg).

    !IF (!TAIL(!1) !NE !NULL) !THEN
        !Parallel3 !TAIL(!1) / !TAIL(!2) / !TAIL(!3).
    !IFEND
!ENDDEFINE.

!Parallel3   1 2 3
            / A B C
            / Alpha Beta Gamma.


*  II.  Generate all pairs of two elements from a list. Again, the    .
*       the action taken on each pair may be anything desired.        .
*       (For clarity, this macro emits a blank line before the output .
*       from each invocation.)                                        .

DEFINE !AllPairs (!POS !CMDEND ).

.  ECHO ' '.

    !LET !Left   =   !HEAD(!1)
    !DO  !Right !IN (!TAIL(!1))
       !LET !Msg = !CONCAT('  ',!Left,' ',!Right)
.     ECHO !QUOTE(!Msg).
    !DOEND

    !IF (!TAIL(!TAIL(!1)) !NE !NULL) !THEN
      !AllPairs !TAIL(!1) .
    !IFEND

!ENDDEFINE.


!AllPairs A B C D E.

!AllPairs A.

=====================
To manage your subscription to SPSSX-L, send a message to
[hidden email] (not to SPSSX-L), with no body text except the
command. To leave the list, send the command
SIGNOFF SPSSX-L
For a list of commands to manage subscriptions, send the command
INFO REFCARD
Please reply to the list and not to my personal email.
Those desiring my consulting or training services please feel free to email me.
---
"Nolite dare sanctum canibus neque mittatis margaritas vestras ante porcos ne forte conculcent eas pedibus suis."
Cum es damnatorum possederunt porcos iens ut salire off sanguinum cliff in abyssum?"
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

Richard Ristow
At 08:36 AM 12/17/2013, David Marso wrote:

>If I were to place an ECHO command after the call to !AllComb would
>that be enough to make this no longer Tail recursion?

Yes, in the strict technical sense. But if the ECHO would be just as
satisfactory before, or if the ECHO doesn't make use of any internal
variables of the caller, it's a trivial instance.

>Can you provide an example of a recursive function which is not tail
>recursion?

OK. You wanted to generate all combinations from a list. Here's a
recursive rule for generating all combinations:

{All combinations of all elements} =
         {All combinations containing the first element} UNION
         {All combinations NOT containing the first element}

Here's a macro implementation. In this macro, "!Set" is a list of
elements, from which all combinations will be generated; "!Fixed" is
a list of elements to be prefixed to all combinations from "!Set".
(Notice that the output can be interpreted as a count-down binary counter.)

DEFINE !AllCombs(Fixed = !CHAREND('/')
                 /Set   = !CMDEND)

    !IF (!Set !EQ !NULL)
     !THEN
.     ECHO !QUOTE(!Fixed).
     !ELSE
       !LET !With    = !CONCAT(!Fixed,' ',!HEAD(!Set))
       !LET !Without = !Fixed
       !AllCombs Fixed = !With    / Set = !TAIL(!Set).
       !AllCombs Fixed = !Without / Set = !TAIL(!Set).
     !IFEND
!ENDDEFINE.

!AllCombs Set=A B C D E.
A B C D E
A B C D
A B C E
A B C
A B D E
A B D
A B E
A B
A C D E
A C D
A C E
A C
A D E
A D
A E
A
B C D E
B C D
B C E
B C
B D E
B D
B E
B
C D E
C D
C E
C
D E
D
E


==================
APPENDIX: All code
==================
*  C:\Documents and Settings\Richard\My Documents  .
*    \Technical\spssx-l\Z-2013\                    .
*    2013-12-17 Marso-Recursion with Macro-D.SPS   .

*  In response to posting                          .
*  Date:    Tue, 17 Dec 2013 05:36:48 -0800        .
*  From:    David Marso <[hidden email]>    .
*  Subject: Re: Recursion with Macro ;-)           .
*  To:      [hidden email]               .

*  ------------------------------------------------------------------ .
*  Note: There are other postings by this author in this thread. And  .
*  this is the fourth example code I've written for posting in this   .
*  thread -- the other three have earlier dates in their names.       .
*  ------------------------------------------------------------------ .

*  "Can you provide an example of a recursive function which is not   .
*  tail recursion?"                                                   .

*  OK. You wanted to generate all combinations from a list. Here's a  .
*  recursive rule for generating all combinations:                    .
*                                                                     .
*  {All combinations of all elements} =                               .
*       {All combinations containing the first element} UNION           .
*       {All combinations NOT containing the first element}             .
*                                                                     .
*  And here's a macro implementation:                                 .

*  (This includes five lines of inactive trace code, immediately      .
*  following the "DEFINE". Those will be dropped from the posted      .
*  illustration.)                                                     .

DEFINE !AllCombs(Fixed = !CHAREND('/')
                 /Set   = !CMDEND)

    !LET !MsgF = !CONCAT('   Fixed = ',!Fixed)
    !LET !MsgS = !CONCAT('   Set   = ',!Set)
*  ECHO 'Entering <!>AllCombs:'.
*  ECHO !QUOTE(!MsgF).
*  ECHO !QUOTE(!MsgS).

    !IF (!Set !EQ !NULL)
     !THEN
.     ECHO !QUOTE(!Fixed).
     !ELSE
       !LET !With    = !CONCAT(!Fixed,' ',!HEAD(!Set))
       !LET !Without = !Fixed
       !AllCombs Fixed = !With    / Set = !TAIL(!Set).
       !AllCombs Fixed = !Without / Set = !TAIL(!Set).
     !IFEND
!ENDDEFINE.

!AllCombs Set=A B C D E.

=====================
To manage your subscription to SPSSX-L, send a message to
[hidden email] (not to SPSSX-L), with no body text except the
command. To leave the list, send the command
SIGNOFF SPSSX-L
For a list of commands to manage subscriptions, send the command
INFO REFCARD
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

Richard Ristow
In reply to this post by Richard Ristow
At 03:21 PM 12/11/2013, I wrote:

>I don't think true recursion is possible in SPSS macros, because
>they have no local storage.

It turns out that this is simply wrong. Macro variables' values *are*
local to the macro they are defined in; and to each instance of that
macro, if it calls itself. As a result, true recursive macros are
possible in SPSS.

See earlier postings in this thread, demonstrating both points.

=====================
To manage your subscription to SPSSX-L, send a message to
[hidden email] (not to SPSSX-L), with no body text except the
command. To leave the list, send the command
SIGNOFF SPSSX-L
For a list of commands to manage subscriptions, send the command
INFO REFCARD
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

David Marso
Administrator
In reply to this post by Richard Ristow

Thank you Richard for making my head spin with that one ;-)
Wonder if there might be an easy way to spit the combos out in an order more of
A, B, C, D, A B ,A C, A D, B C, B D, C D, A B C, A B D, A C D, B C D, A B C D
Here is a somewhat more mundane example.  
This one generates nested LOOPS of an arbitrary depth.
One could of course do this with a simple !DO with the attendant !HEAD !TAIL parsing of the second 2 lists.  But I'm liking this approach MORE and MORE as I twist my brain around the implications.
I also tried massaging this into a single macro but couldn't quite resolve the END CASE at the right point.

DEFINE !NestLoop (!POS !CHAREND ("/")
                / !POS !CHAREND ("/")
                / !POS !CHAREND ("/")
                / !POS !CMDEND !DEFAULT(1)).
!IF (!LENGTH(!2) !GT 0) !THEN
LOOP !CONCAT(X,!LENGTH(!1))=!HEAD(!2) TO !HEAD(!3) BY !HEAD(!4).
!NestLoop !CONCAT(!1,#)/!TAIL(!2)/!TAIL(!3)/!tail(!4) .
!IFEND
!ENDDEFINE.

DEFINE !UnNest (!POS !CMDEND).      
!IF (!LENGTH(!1) !GT 0) !THEN
END LOOP.
!UnNest !TAIL(!1) .
!IFEND
!ENDDEFINE.

DEFINE !Nested (!POS !CHAREND ("/")
                /!POS !CHAREND ("/")
                /!POS !CMDEND !DEFAULT(1)).              
NEW FILE.
INPUT PROGRAM.
!NestLoop  /!1 /!2 /!3.
LEAVE ALL.
END CASE.
!UnNest !1 .
END FILE.
END INPUT PROGRAM.
!ENDDEFINE.

SET PRINTBACK ON MPRINT ON.
!Nested   1 2 3 4 5 6  7 8 9  9
         /1 5 6 8 8 9 10 9 9 10
         /1 1 1 2 1 1  2 1 1  1.

EXECUTE.
Richard Ristow wrote
At 08:36 AM 12/17/2013, David Marso wrote:

>If I were to place an ECHO command after the call to !AllComb would
>that be enough to make this no longer Tail recursion?

Yes, in the strict technical sense. But if the ECHO would be just as
satisfactory before, or if the ECHO doesn't make use of any internal
variables of the caller, it's a trivial instance.

>Can you provide an example of a recursive function which is not tail
>recursion?

OK. You wanted to generate all combinations from a list. Here's a
recursive rule for generating all combinations:

{All combinations of all elements} =
         {All combinations containing the first element} UNION
         {All combinations NOT containing the first element}

Here's a macro implementation. In this macro, "!Set" is a list of
elements, from which all combinations will be generated; "!Fixed" is
a list of elements to be prefixed to all combinations from "!Set".
(Notice that the output can be interpreted as a count-down binary counter.)

DEFINE !AllCombs(Fixed = !CHAREND('/')
                 /Set   = !CMDEND)

    !IF (!Set !EQ !NULL)
     !THEN
.     ECHO !QUOTE(!Fixed).
     !ELSE
       !LET !With    = !CONCAT(!Fixed,' ',!HEAD(!Set))
       !LET !Without = !Fixed
       !AllCombs Fixed = !With    / Set = !TAIL(!Set).
       !AllCombs Fixed = !Without / Set = !TAIL(!Set).
     !IFEND
!ENDDEFINE.

!AllCombs Set=A B C D E.
A B C D E
A B C D
A B C E
A B C
A B D E
A B D
A B E
A B
A C D E
A C D
A C E
A C
A D E
A D
A E
A
B C D E
B C D
B C E
B C
B D E
B D
B E
B
C D E
C D
C E
C
D E
D
E


==================
APPENDIX: All code
==================
*  C:\Documents and Settings\Richard\My Documents  .
*    \Technical\spssx-l\Z-2013\                    .
*    2013-12-17 Marso-Recursion with Macro-D.SPS   .

*  In response to posting                          .
*  Date:    Tue, 17 Dec 2013 05:36:48 -0800        .
*  From:    David Marso <[hidden email]>    .
*  Subject: Re: Recursion with Macro ;-)           .
*  To:      [hidden email]               .

*  ------------------------------------------------------------------ .
*  Note: There are other postings by this author in this thread. And  .
*  this is the fourth example code I've written for posting in this   .
*  thread -- the other three have earlier dates in their names.       .
*  ------------------------------------------------------------------ .

*  "Can you provide an example of a recursive function which is not   .
*  tail recursion?"                                                   .

*  OK. You wanted to generate all combinations from a list. Here's a  .
*  recursive rule for generating all combinations:                    .
*                                                                     .
*  {All combinations of all elements} =                               .
*       {All combinations containing the first element} UNION           .
*       {All combinations NOT containing the first element}             .
*                                                                     .
*  And here's a macro implementation:                                 .

*  (This includes five lines of inactive trace code, immediately      .
*  following the "DEFINE". Those will be dropped from the posted      .
*  illustration.)                                                     .

DEFINE !AllCombs(Fixed = !CHAREND('/')
                 /Set   = !CMDEND)

    !LET !MsgF = !CONCAT('   Fixed = ',!Fixed)
    !LET !MsgS = !CONCAT('   Set   = ',!Set)
*  ECHO 'Entering <!>AllCombs:'.
*  ECHO !QUOTE(!MsgF).
*  ECHO !QUOTE(!MsgS).

    !IF (!Set !EQ !NULL)
     !THEN
.     ECHO !QUOTE(!Fixed).
     !ELSE
       !LET !With    = !CONCAT(!Fixed,' ',!HEAD(!Set))
       !LET !Without = !Fixed
       !AllCombs Fixed = !With    / Set = !TAIL(!Set).
       !AllCombs Fixed = !Without / Set = !TAIL(!Set).
     !IFEND
!ENDDEFINE.

!AllCombs Set=A B C D E.

=====================
To manage your subscription to SPSSX-L, send a message to
[hidden email] (not to SPSSX-L), with no body text except the
command. To leave the list, send the command
SIGNOFF SPSSX-L
For a list of commands to manage subscriptions, send the command
INFO REFCARD
Please reply to the list and not to my personal email.
Those desiring my consulting or training services please feel free to email me.
---
"Nolite dare sanctum canibus neque mittatis margaritas vestras ante porcos ne forte conculcent eas pedibus suis."
Cum es damnatorum possederunt porcos iens ut salire off sanguinum cliff in abyssum?"
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

Richard Ristow
At 11:26 AM 12/31/2013, David Marso wrote:

>This [macro set] generates nested LOOPS of an arbitrary depth.
>I tried massaging this into a single macro but couldn't quite
>resolve the END CASE at the right point.

Here's a one-macro variation that appears to work. It is,
technically, recursive rather than tail-recursive, because it takes
an action (emitting "END LOOP") after calling itself. However, this
should be considered a trivial case, since the action taken does not
depend on the macro's internal state being preserved when it invokes itself.

A side issue: your macro set emitted a lot of extraneous periods and
blank lines. I've reduced the number of periods emitted by not
putting periods after the declaration of the macro arguments, or
after the !ENDDEFINE. However, there are still some, and I don't see
where they come from. Can you solve that one?

DEFINE !NestLoop (!POS !CHAREND ("/")
                 / !POS !CHAREND ("/")
                 / !POS !CHAREND ("/")
                 / !POS !CMDEND !DEFAULT(1))

!IF (!2 !NE !NULL) !THEN
   !LET  !Name = !CONCAT(X,!LENGTH(!1))
.  LOOP !Name = !HEAD(!2) TO !HEAD(!3) BY !HEAD(!4).
   !NestLoop !CONCAT(!1,#)/!TAIL(!2)/!TAIL(!3)/!tail(!4) .
.  END LOOP.
!ELSE.
.  LEAVE ALL.
.  END CASE.
!IFEND
!ENDDEFINE

PRESERVE.
SET PRINTBACK ON MPRINT ON.
NEW FILE.
   51 M>  NEW FILE.
INPUT PROGRAM.
   52 M>  INPUT PROGRAM.

   53 M>
!NestLoop  /1 2 3 4 5 6  7 8 9  9
            /1 5 6 8 8 9 10 9 9 10
            /1 1 1 2 1 1  2 1 1  1.
   55 M>
   56 M>  .
   57 M>  . LOOP X0 = 1 TO 1 BY 1.
   58 M>
   59 M>  .
   60 M>  . LOOP X1 = 2 TO 5 BY 1.
   61 M>
   62 M>  .
   63 M>  . LOOP X2 = 3 TO 6 BY 1.
   64 M>
   65 M>  .
   66 M>  . LOOP X3 = 4 TO 8 BY 2.
   67 M>
   68 M>  .
   69 M>  . LOOP X4 = 5 TO 8 BY 1.
   70 M>
   71 M>  .
   72 M>  . LOOP X5 = 6 TO 9 BY 1.
   73 M>
   74 M>  .
   75 M>  . LOOP X6 = 7 TO 10 BY 2.
   76 M>
   77 M>  .
   78 M>  . LOOP X7 = 8 TO 9 BY 1.
   79 M>
   80 M>  .
   81 M>  . LOOP X8 = 9 TO 9 BY 1.
   82 M>
   83 M>  .
   84 M>  . LOOP X9 = 9 TO 10 BY 1.
   85 M>
   86 M>  .
   87 M>
   88 M>  LEAVE ALL.
   89 M>  END CASE.
   90 M>  .
   91 M>  END LOOP.
   92 M>  .
   93 M>  END LOOP.
   94 M>  .
   95 M>  END LOOP.
   96 M>  .
   97 M>  END LOOP.
   98 M>  .
   99 M>  END LOOP.
  100 M>  .
  101 M>  END LOOP.
  102 M>  .
  103 M>  END LOOP.
  104 M>  .
  105 M>  END LOOP.
  106 M>  .
  107 M>  END LOOP.
  108 M>  .
  109 M>  END LOOP.
  110 M>  .

  111 M>
END FILE.
  112 M>  END FILE.
END INPUT PROGRAM.
  113 M>  END INPUT PROGRAM.

  114 M>
RESTORE.
  115 M>  RESTORE.
EXECUTE.
SHOW N.

SHOW
|-----------------------------|---------------------------|
|Output Created               |02-JAN-2014 23:15:37       |
|-----------------------------|---------------------------|
  System Settings
|-------|---------------|-------|
|Keyword|Description    |Setting|
|-------|---------------|-------|
|N      |Number of cases|6144   |
|       |in the working |       |
|       |data file      |       |
|-------|---------------|-------|
==================
APPENDIX: All code
==================
*  C:\Documents and Settings\Richard\My Documents  .
*    \Technical\spssx-l\Z-2013\                    .
*    2013-12-31 Marso-Recursion with Macro-E.SPS   .

*  In response to posting                          .
*  Date:    Tue, 31 Dec 2013 08:26:31 -0800        .
*  From:    David Marso <[hidden email]>    .
*  Subject: Re: Recursion with Macro ;-)           .
*  To:      [hidden email]               .

*  "This [macro] generates nested LOOPS of an arbitrary depth. I     .
*  also tried massaging this into a single macro but couldn't quite  .
*  resolve the END CASE at the right point."                         .

*  See the original posting for David's code. This attempts a        .
*  single-macro solution, as well as making some changes to match my .
*  style.                                                            .
*                                                                    .
*  Incidentally, it drops the '.' that David usually uses at the end .
*  of the DEFINE clause, before the macro body. That period is not   .
*  needed, so it is emitted as part of the macro output.             .


DEFINE !NestLoop (!POS !CHAREND ("/")
                 / !POS !CHAREND ("/")
                 / !POS !CHAREND ("/")
                 / !POS !CMDEND !DEFAULT(1))

!IF (!2 !NE !NULL) !THEN
   !LET  !Name = !CONCAT(X,!LENGTH(!1))
.  LOOP !Name = !HEAD(!2) TO !HEAD(!3) BY !HEAD(!4).
   !NestLoop !CONCAT(!1,#)/!TAIL(!2)/!TAIL(!3)/!tail(!4) .
.  END LOOP.
!ELSE.
.  LEAVE ALL.
.  END CASE.
!IFEND
!ENDDEFINE

PRESERVE.
SET PRINTBACK ON MPRINT ON.
NEW FILE.
INPUT PROGRAM.

!NestLoop  /1 2 3 4 5 6  7 8 9  9
            /1 5 6 8 8 9 10 9 9 10
            /1 1 1 2 1 1  2 1 1  1.

END FILE.
END INPUT PROGRAM.

RESTORE.
EXECUTE.

SHOW N.

=====================
To manage your subscription to SPSSX-L, send a message to
[hidden email] (not to SPSSX-L), with no body text except the
command. To leave the list, send the command
SIGNOFF SPSSX-L
For a list of commands to manage subscriptions, send the command
INFO REFCARD
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

Richard Ristow
Correction. At 11:27 PM 1/2/2014, I wrote:

>Here's a one-macro [solution] It is recursive rather than
>tail-recursive, because it takes an action (emitting "END LOOP")
>after calling itself. However, this should be considered a trivial
>case, since the action taken does not depend on the macro's internal
>state being preserved when it invokes itself.

It is actually truly, not trivially, recursive, because it *does*
depend on state information being retained: namely, how deeply the
particular call is nested. That's how it emits the right number of
"END LOOP" statements.

=====================
To manage your subscription to SPSSX-L, send a message to
[hidden email] (not to SPSSX-L), with no body text except the
command. To leave the list, send the command
SIGNOFF SPSSX-L
For a list of commands to manage subscriptions, send the command
INFO REFCARD
Reply | Threaded
Open this post in threaded view
|

Re: Recursion with Macro ;-)

David Marso
Administrator
In reply to this post by Richard Ristow
Thanks Richard,
Very nice!
"Can you solve that one?"
Sure:
SET MPRINT OFF PRINTBACK OFF ;-))
Actually have no idea where those are emanating from.
Odd that after over 20 years of using Macros I had never thought of trying out these recursion concepts.
I'd better not see them popping up in any books without being credited (unlike past history)!!!
--

Richard Ristow wrote
At 11:26 AM 12/31/2013, David Marso wrote:

>This [macro set] generates nested LOOPS of an arbitrary depth.
>I tried massaging this into a single macro but couldn't quite
>resolve the END CASE at the right point.

Here's a one-macro variation that appears to work. It is,
technically, recursive rather than tail-recursive, because it takes
an action (emitting "END LOOP") after calling itself. However, this
should be considered a trivial case, since the action taken does not
depend on the macro's internal state being preserved when it invokes itself.

A side issue: your macro set emitted a lot of extraneous periods and
blank lines. I've reduced the number of periods emitted by not
putting periods after the declaration of the macro arguments, or
after the !ENDDEFINE. However, there are still some, and I don't see
where they come from. Can you solve that one?

DEFINE !NestLoop (!POS !CHAREND ("/")
                 / !POS !CHAREND ("/")
                 / !POS !CHAREND ("/")
                 / !POS !CMDEND !DEFAULT(1))

!IF (!2 !NE !NULL) !THEN
   !LET  !Name = !CONCAT(X,!LENGTH(!1))
.  LOOP !Name = !HEAD(!2) TO !HEAD(!3) BY !HEAD(!4).
   !NestLoop !CONCAT(!1,#)/!TAIL(!2)/!TAIL(!3)/!tail(!4) .
.  END LOOP.
!ELSE.
.  LEAVE ALL.
.  END CASE.
!IFEND
!ENDDEFINE

PRESERVE.
SET PRINTBACK ON MPRINT ON.
NEW FILE.
   51 M>  NEW FILE.
INPUT PROGRAM.
   52 M>  INPUT PROGRAM.

   53 M>
!NestLoop  /1 2 3 4 5 6  7 8 9  9
            /1 5 6 8 8 9 10 9 9 10
            /1 1 1 2 1 1  2 1 1  1.
   55 M>
   56 M>  .
   57 M>  . LOOP X0 = 1 TO 1 BY 1.
   58 M>
   59 M>  .
   60 M>  . LOOP X1 = 2 TO 5 BY 1.
   61 M>
   62 M>  .
   63 M>  . LOOP X2 = 3 TO 6 BY 1.
   64 M>
   65 M>  .
   66 M>  . LOOP X3 = 4 TO 8 BY 2.
   67 M>
   68 M>  .
   69 M>  . LOOP X4 = 5 TO 8 BY 1.
   70 M>
   71 M>  .
   72 M>  . LOOP X5 = 6 TO 9 BY 1.
   73 M>
   74 M>  .
   75 M>  . LOOP X6 = 7 TO 10 BY 2.
   76 M>
   77 M>  .
   78 M>  . LOOP X7 = 8 TO 9 BY 1.
   79 M>
   80 M>  .
   81 M>  . LOOP X8 = 9 TO 9 BY 1.
   82 M>
   83 M>  .
   84 M>  . LOOP X9 = 9 TO 10 BY 1.
   85 M>
   86 M>  .
   87 M>
   88 M>  LEAVE ALL.
   89 M>  END CASE.
   90 M>  .
   91 M>  END LOOP.
   92 M>  .
   93 M>  END LOOP.
   94 M>  .
   95 M>  END LOOP.
   96 M>  .
   97 M>  END LOOP.
   98 M>  .
   99 M>  END LOOP.
  100 M>  .
  101 M>  END LOOP.
  102 M>  .
  103 M>  END LOOP.
  104 M>  .
  105 M>  END LOOP.
  106 M>  .
  107 M>  END LOOP.
  108 M>  .
  109 M>  END LOOP.
  110 M>  .

  111 M>
END FILE.
  112 M>  END FILE.
END INPUT PROGRAM.
  113 M>  END INPUT PROGRAM.

  114 M>
RESTORE.
  115 M>  RESTORE.
EXECUTE.
SHOW N.

SHOW
|-----------------------------|---------------------------|
|Output Created               |02-JAN-2014 23:15:37       |
|-----------------------------|---------------------------|
  System Settings
|-------|---------------|-------|
|Keyword|Description    |Setting|
|-------|---------------|-------|
|N      |Number of cases|6144   |
|       |in the working |       |
|       |data file      |       |
|-------|---------------|-------|
==================
APPENDIX: All code
==================
*  C:\Documents and Settings\Richard\My Documents  .
*    \Technical\spssx-l\Z-2013\                    .
*    2013-12-31 Marso-Recursion with Macro-E.SPS   .

*  In response to posting                          .
*  Date:    Tue, 31 Dec 2013 08:26:31 -0800        .
*  From:    David Marso <[hidden email]>    .
*  Subject: Re: Recursion with Macro ;-)           .
*  To:      [hidden email]               .

*  "This [macro] generates nested LOOPS of an arbitrary depth. I     .
*  also tried massaging this into a single macro but couldn't quite  .
*  resolve the END CASE at the right point."                         .

*  See the original posting for David's code. This attempts a        .
*  single-macro solution, as well as making some changes to match my .
*  style.                                                            .
*                                                                    .
*  Incidentally, it drops the '.' that David usually uses at the end .
*  of the DEFINE clause, before the macro body. That period is not   .
*  needed, so it is emitted as part of the macro output.             .


DEFINE !NestLoop (!POS !CHAREND ("/")
                 / !POS !CHAREND ("/")
                 / !POS !CHAREND ("/")
                 / !POS !CMDEND !DEFAULT(1))

!IF (!2 !NE !NULL) !THEN
   !LET  !Name = !CONCAT(X,!LENGTH(!1))
.  LOOP !Name = !HEAD(!2) TO !HEAD(!3) BY !HEAD(!4).
   !NestLoop !CONCAT(!1,#)/!TAIL(!2)/!TAIL(!3)/!tail(!4) .
.  END LOOP.
!ELSE.
.  LEAVE ALL.
.  END CASE.
!IFEND
!ENDDEFINE

PRESERVE.
SET PRINTBACK ON MPRINT ON.
NEW FILE.
INPUT PROGRAM.

!NestLoop  /1 2 3 4 5 6  7 8 9  9
            /1 5 6 8 8 9 10 9 9 10
            /1 1 1 2 1 1  2 1 1  1.

END FILE.
END INPUT PROGRAM.

RESTORE.
EXECUTE.

SHOW N.

=====================
To manage your subscription to SPSSX-L, send a message to
[hidden email] (not to SPSSX-L), with no body text except the
command. To leave the list, send the command
SIGNOFF SPSSX-L
For a list of commands to manage subscriptions, send the command
INFO REFCARD
Please reply to the list and not to my personal email.
Those desiring my consulting or training services please feel free to email me.
---
"Nolite dare sanctum canibus neque mittatis margaritas vestras ante porcos ne forte conculcent eas pedibus suis."
Cum es damnatorum possederunt porcos iens ut salire off sanguinum cliff in abyssum?"