subroutine calling itself + passing by reference

I have a subroutine (or function section) that needs to make a check and if its true, change some data and restart itself. The problem is the subroutine needs to be passed some parameters from the main function. Obviously if I need to change more than one, I should use pass by reference, but what about when it (possibly) calls itself again? Can it pass those same parameters back to itself, even after it has changed them?

Main function()
stuff
stuff
subroutine_1(param1,param2)
more stuff
end main function

function subroutine_1(para1,para2)
variable &para1, &para2
stuff
if true
...change data, including para1
...subroutine_1(????) <-what goes in here?
else
...other stuff
...break
end subroutine_1

This would be a simple goto statement, but I guess those died with the dinosaurs. Is this even the way to go about with this, or are there more clever solutions? I already keep most of my variables as waves as it is to avoid the whole local/global var mashup.. Sorry if this is a simple question, I havent programmed since my days in C++ and we never got to object orientated..
daggaz wrote:
I have a subroutine (or function section) that needs to make a check and if its true, change some data and restart itself. ...


Try this to see a brief example of self-iteration on a function ...

#pragma rtGlobals=1     // Use modern global access method.

Function Main()

    variable ac = 0, bc = 0
    make/O/N=10 test=0
    change(ac,bc)
    return 0
end

Function Change(ac,bc)
    variable ac, bc
   
    wave test
    test = ac*p + bc
    doupdate
    string pStr = "Do you want to increment ac (YES) or bc (NO) or neither (CANCEL) with " + num2str(ac) + ", " + num2str(bc) + "?"
    DoAlert/T="Tell Me So", 2, pStr
    switch(V_flag)
        case 1:
            ac += 1
            Change(ac,bc)
            break
        case 2:
            bc += 1
            Change(ac,bc)
            break
        case 3:
            break
    endswitch
    return 0
end


After you put this in to the procedure window, do this in the command line

make/O/N=10 test=0
display test
main()


--
J. J. Weimer
Chemistry / Chemical & Materials Engineering, UAHuntsville
Thanks for the example. This whole time, I thought the parameters _had_ to have different names between the main and the subroutine, hence my consternation when it passes to itself.
I could swear I have run afoul of just that before, but maybe .. i dunno... thanks =)
Also note ... pass by reference is probably useful when the Main() function has to know the changed values that are passed to the Change() function, akin to ...

Function Main()
     variable ac=0, bc=0
     ... use the initial values of ac and bc somehow
     Change(ac,bc)
     ... use the changed values of ac and bc somehow
    return 0
end


--
J. J. Weimer
Chemistry / Chemical & Materials Engineering, UAHuntsville
Yeah, I think I understood the pass by reference bit (seemed clear enough in the help files at least)...

Curious about the IF loop, as apparently I cant use a break statement inside of one. Am I forced to use a case loop, or will IGOR understand to drop the original subroutine when the subroutine calls itself? Or will it run the second iteration of subroutine, and then go back and finish the first iteration? Im hoping its similar to the return statement and is the former and not the latter case.
After finishing the second interation it will return to complete the first. If the function calls itself recursively multiple times, it will then climb back up the recursion chain (hope that's a good metaphor) finishing out each call to itself. You might think about using a flag to let the function know that already done it's job... it that is what you have in mind for it's function. You might also consider the use of optional parameters for the purpose of the function communicating to itself.
daggaz wrote:
Yeah, I think I understood the pass by reference bit (seemed clear enough in the help files at least)...

Curious about the IF loop, as apparently I cant use a break statement inside of one. Am I forced to use a case loop, or will IGOR understand to drop the original subroutine when the subroutine calls itself? Or will it run the second iteration of subroutine, and then go back and finish the first iteration? Im hoping its similar to the return statement and is the former and not the latter case.


Break statements do not exist in if expressions because there is no iteration taking place - the code only runs once. If you want to break out of an if statement, that simply means that you should introduce another conditional.

For example:
if (some_condition)
    i = 3
    myWave = 4
    if (something_else)
        break   // illegal
    i = 4
endif

Then you should achieve this using
if (some_condition)
    i = 3
    myWave = 4
    if (!something_else)
        i = 4
    endif
endif


I'm not sure where you're stuck with your scheme. What you're trying to do, a function calling itself, is known as recursion, and it is an accepted technique in every programming language I've used.

Perhaps it would help to realize that, when you call a function in Igor, Igor immediately starts running that function, and will only return to the original function when that function finishes. In other words, functions are never interleaved.

Also, remember that local variables and strings do not have names - they're simply tiny regions of memory somewhere in your computer's memory. Variable names exist so Igor knows what location you're referring to when you're coding, but every function can use whatever names it wants. It's simply an agreement between you and the Igor compiler. When you actually run the code there is no such thing as variable names or string names.

The exceptions are global variables, of course.
One other thought...

You can use the return statement to exit from an if... endif block.
Make sure any loose ends are tied up before doing so, however.

Another thought...

I think that when he writes
741 wrote:
Perhaps it would help to realize that, when you call a function in Igor, Igor immediately starts running that function, and will only return to the original function when that function finishes. In other words, functions are never interleaved.

Also, remember that local variables and strings do not have names - they're simply tiny regions of memory somewhere in your computer's memory. Variable names exist so Igor knows what location you're referring to when you're coding, but every function can use whatever names it wants. It's simply an agreement between you and the Igor compiler. When you actually run the code there is no such thing as variable names or strings.


741 is saying that when a function is called recursively, the second and any following instances of that function operate independently of the the preceding instances of that function. They do not share local variables. For each instance of the function, a separate block of memory is allocated in which that function exists and plays out its life.

Yeah, sorry for the confusion i know im not very clear myself. What I want to do is ensure that when the subroutine calls itself, the original instance of that subroutine is terminated, rather than waiting in the background only to resume once the second instance has run its course. Obviously break is the wrong command, as I realize now it only stops an iteration, rather than stopping the function itself (which is what I thought it did).

daggaz wrote:
What I want to do is ensure that when the subroutine calls itself, the original instance of that subroutine is terminated, ...


Then, pass by reference might be what you need. The change function should return a value that tells whether to call itself again from the main function. Something akin to this ...

Function Main()

    variable redoit = 0
    variable ac = 0, bc = 0
    make/O/N=10 mywave=0
    do
        redoit = Change(mywave,ac,bc)
         ac += 1
         bc *= 3
     while(redoit=0)
    return 0
end

Function Change(mywave,ac,bc)
    wave mywave
    variable $ac, $bc

     mywave = ac*p + bc
    if (mywave[0] <= 10)
       return 1  // redo the calculations until first value is larger than 10
    else
       return 0  // do not redo the calculations
    endif
end


--
J. J. Weimer
Chemistry / Chemical & Materials Engineering, UAHuntsville
daggaz-

I can't help thinking that there might be a more straightforward way to code what you want to do, if you could describe the result you're trying to get. Instead of asking about implementation details (as interesting as they might be) perhaps we could help you find an alternate approach to your problem.

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com
Hi John, thanks.

Hmm not sure how to explain it better than thru implementation details.. but I'll give it a try.

I made a program (nearly finished, will upload it soon) which generates synthetic data of sub-diffraction micrograph images in order to test some of our private lab software. The user inputs a number of parameters (the list keeps growing), and the program gets to work generating data. The data consist of randomly placed 2D gaussian profiles (with noise) generated randomly from within the parameter ranges. Each iteration of the program produces two "micrographs"; one with "vesicles", and another with matching "protein" signatures for a specifiéd percent of those vesicles, along with additional nonlocalized signatures representing non-specific binding. By localized, I mean the particle centers between the two images are identical.

In order to optimize the speed (it was real slow in the first build), I divide each image array into zones. When I generate a particle, I then "roll" for a zone, so as to avoid iterating over the entire image. Thing is, with the zones it is now more likely to fill the space (there is logic to avoid overlapping the particles) and this would break my code. The simplest and most logical solution I could think of, was to check each zone for available target pixels based on the incoming particle, something which needs to be done anyhow for targetting. If the available pixels cant accomodate the particle, I simply shrink the particle to match. But if there are no available pixels even for the smallest particles, I need to remove the zone from the list (each zone is actually a set of corner coordinates saved in wave form) so as not to roll it again.

So my zonechecker function rolls a zone, checks it for targets, and if there are none, it needs to kill that zone and restart itself. Otherwise it picks a target pixel and passes that info to the particle drawing subroutine which uses it as the center point. It really is just a simple goto loop that is needed, but things got complicated with object orientated programming.

Well.. thats a lot of words and Im not sure if it helps or if its what you meant, but there ya go =P
I have not followed all of the twists and turns but it seems to me that all you need is a simple loop, as illustrated by jjweimers post above.
daggaz wrote:
...
So my zonechecker function rolls a zone, checks it for targets, and if there are none, it needs to kill that zone and restart itself. Otherwise it picks a target pixel and passes that info to the particle drawing subroutine which uses it as the center point. It really is just a simple goto loop that is needed, but things got complicated with object orientated programming.


If I break this down to avoid GoTo, I get something like this ...

* get data image
* get user input parameters
* create empty model image
* break model image in to zones
-> Loop on All Desired Zones
** create a new zone for model image using input parameters
** check it for targets against data image
--> if (targets exist)
*** save this zone
*** exit this loop
--> else
*** kill this zone
*** exit this loop
-> End Loop on All Desired Zones
* stitch together all viable zones to model image
* display model image

Is this helpful?

--
J. J. Weimer
Chemistry / Chemical & Materials Engineering, UAHuntsville
heheh.. not quite. I think I will work in a loop as posted above, specially considering the program is up and running and a massive structural reboot to fix a single logic point is not on the menu. Thanks tho for trying.