Hi,
I am trying to generate all possible combinations from a set of variables, basically n choose r combinations, and have been trying to use the following set of codes which was adapted from other softwares.
The code ends up giving me with the following strings "abc", abd", "ab" and "acd" and then "acd" ^ infinitity, while the correct combinations i am trying to store into the svector are only "abc", "abd" and "acd". Can i get someone's guidance on the correct usage of subroutines, please?
Thanks and regards,
'------------------------------------
!sv = 1
svector(3) sofar_str
subroutine combinationNR(string %sofar, string %rest, scalar !n)
if !n=0 then
sofar_str(!sv) = %sofar
!sv = !sv + 1
else
for !i = 1 to @length(%rest)
call combinationNR(%sofar+@wmid(%rest,!i,1), @wmid(%rest,!i+1), !n-1)
next
endif
endsub
call combinationNR("", "a b c d", 3)
'-------------------------
Endless Subroutine
Moderators: EViews Gareth, EViews Moderator, EViews Jason, EViews Matt
-
EViews Matt
- EViews Developer
- Posts: 584
- Joined: Thu Apr 25, 2013 7:48 pm
Re: Endless Subroutine
Hello,
There are a pair of issues primarily responsible for the difficulties you're experiencing.
First, inside the for loop, your use of @wmid() assumes that variable !i is an element index, but in the loop's condition !i will iterate through all of %rest's character indices. For example, in the initial invocation of combinationNR() when %rest is "a b c d", you want !i to iterate between 1 and 4, not 1 and 7. In short, you need the upper limit on !i to be the number of elements in %rest, not the number of characters in %rest. This can be accomplished by replacing @length() with @wcount(). As is, the extra calls to @wmid() return the empty string, which is why some of the generated results are missing letters.
Second, also related to the for loop, your use of recursion assumes that variable !i is a local variable and therefore cannot be changed within any recursive call. Unfortunately, !i is a global variable and therefore is modified within recursive calls, which seriously disrupts the flow of the recursion. In order to make !i behave like a local variable the combinationNR() subroutine needs to be made a local subroutine. However, after making that change combinationNR() will lose access to the global !sv and sofar_str, but you can pass them to the subroutine instead.
And FYI, you should expect four resulting strings from your example, abc, abd, acd, and bcd. 4 choose 3 = 4.
There are a pair of issues primarily responsible for the difficulties you're experiencing.
First, inside the for loop, your use of @wmid() assumes that variable !i is an element index, but in the loop's condition !i will iterate through all of %rest's character indices. For example, in the initial invocation of combinationNR() when %rest is "a b c d", you want !i to iterate between 1 and 4, not 1 and 7. In short, you need the upper limit on !i to be the number of elements in %rest, not the number of characters in %rest. This can be accomplished by replacing @length() with @wcount(). As is, the extra calls to @wmid() return the empty string, which is why some of the generated results are missing letters.
Second, also related to the for loop, your use of recursion assumes that variable !i is a local variable and therefore cannot be changed within any recursive call. Unfortunately, !i is a global variable and therefore is modified within recursive calls, which seriously disrupts the flow of the recursion. In order to make !i behave like a local variable the combinationNR() subroutine needs to be made a local subroutine. However, after making that change combinationNR() will lose access to the global !sv and sofar_str, but you can pass them to the subroutine instead.
And FYI, you should expect four resulting strings from your example, abc, abd, acd, and bcd. 4 choose 3 = 4.
Last edited by EViews Matt on Wed Nov 16, 2016 3:29 pm, edited 3 times in total.
Re: Endless Subroutine
Hi Matt,
Thanks much for the guidance! I have tried using both the local and global subroutines as per the code appended below.
When i do not use local, only "abc" and "abd" are generated and when i include local, nothing is generated at all. Could you let me know what else did i miss out in the code please?
'----------------------------------------------
subroutine [local] combinationNR(string %sofar, string %rest, scalar !n)
svector(4) sofar_str
if !n = 0 then
sofar_str(!i) = %sofar
else
for !i = 1 to @wcount(%rest)
call combinationNR(%sofar+@wmid(%rest,!i,1), @wmid(%rest,!i+1), !n-1)
next
endif
endsub
call combinationNR("", "a b c d", 3)
'------------------------------------
Thanks much for the guidance! I have tried using both the local and global subroutines as per the code appended below.
When i do not use local, only "abc" and "abd" are generated and when i include local, nothing is generated at all. Could you let me know what else did i miss out in the code please?
'----------------------------------------------
subroutine [local] combinationNR(string %sofar, string %rest, scalar !n)
svector(4) sofar_str
if !n = 0 then
sofar_str(!i) = %sofar
else
for !i = 1 to @wcount(%rest)
call combinationNR(%sofar+@wmid(%rest,!i,1), @wmid(%rest,!i+1), !n-1)
next
endif
endsub
call combinationNR("", "a b c d", 3)
'------------------------------------
Re: Endless Subroutine
ok, i found a solution in another thread. can a subroutine be used on an svector (i.e. subroutine(svector %xx))?
'------------------------
subroutine Combinations(string %prefix, string %list_elements, scalar !index, scalar !p)
if !p=0 then
varlist_{!r}(!sv) = %prefix
!sv = !sv + 1
return
endif
if !index=@wcount(%list_elements)+1 then
return
endif
%element=@word(%list_elements,!index)
if @isempty(%prefix)=0 then
%prefix=%prefix + " " + %element
else
%prefix=%element
endif
call Combinations(%prefix, %list_elements, !index+1, !p-1)
%prefix=@wleft(%prefix,@wcount(%prefix)-1)
call Combinations(%prefix, %list_elements, !index+1, !p)
endsub
'-------------------------
'------------------------
subroutine Combinations(string %prefix, string %list_elements, scalar !index, scalar !p)
if !p=0 then
varlist_{!r}(!sv) = %prefix
!sv = !sv + 1
return
endif
if !index=@wcount(%list_elements)+1 then
return
endif
%element=@word(%list_elements,!index)
if @isempty(%prefix)=0 then
%prefix=%prefix + " " + %element
else
%prefix=%element
endif
call Combinations(%prefix, %list_elements, !index+1, !p-1)
%prefix=@wleft(%prefix,@wcount(%prefix)-1)
call Combinations(%prefix, %list_elements, !index+1, !p)
endsub
'-------------------------
-
EViews Matt
- EViews Developer
- Posts: 584
- Joined: Thu Apr 25, 2013 7:48 pm
Re: Endless Subroutine
Hello,
Yes, you can pass an svector to a subroutine. That's actually what I was suggesting you do after making the subroutine local. Here's your original code with the necessary modifications:
There are certainly other ways to recursively generate string combinations, but the above is a pretty minimal change to your original code that accomplishes what I believe you asked for. Note that the !sv variable and sofar_str object still are created outside of the subroutine. This is particularly important for the sofar_str object so that it will exist after the subroutine terminates. Moving sofar_str inside a local subroutine, as one of your subsequent examples did, would create multiple local svectors (one per invocation of combinationNR()) and therefore wouldn't accumulate all the resulting combinations in a single place. Plus, those svectors would be deleted automatically, so you'd lose the results anyway. Passing !sv and sofar_str as above basically permits all invocations of combinationNR() to refer to the global !sv and sofar_str, which otherwise wouldn't be possible within a local subroutine.
Yes, you can pass an svector to a subroutine. That's actually what I was suggesting you do after making the subroutine local. Here's your original code with the necessary modifications:
Code: Select all
!sv = 1
svector(4) sofar_str
subroutine local combinationNR(string %sofar, string %rest, scalar !n, scalar !sv, svector sofar_str)
if !n=0 then
sofar_str(!sv) = %sofar
!sv = !sv + 1
else
for !i = 1 to @wcount(%rest)
call combinationNR(%sofar+@wmid(%rest,!i,1), @wmid(%rest,!i+1), !n-1, !sv, sofar_str)
next
endif
endsub
call combinationNR("", "a b c d", 3, !sv, sofar_str)
Re: Endless Subroutine
Wow, that is great. Thanks much for the kind assistance!
Who is online
Users browsing this forum: No registered users and 2 guests
