Jump to content

Recursive Functions and Variables


drum
 Share

Recommended Posts

Having spent the afternoon debugging a recursive function I wrote, am I right in thinking that local variables are shared amongst all instances of a function. i.e. be very very careful writing a recursive loop. :

Link to comment
Share on other sites

  • Moderators

drum,

Normally I would not expect Local variables to be shared between recursive functions. But if you post a short reproducer of what exactly you are trying to do you we can be more precise. :)

Certainly you do need to be very careful with recursion. Have you read the Recursion tutorial in the Wiki? :huh:

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to comment
Share on other sites

drum,

Interesting question. Per doc a variable local to a function is destroyed when the function ends. Now the kicker, does a recursed function ever end?

kylomas

edit: stupid statement as bowmore has demonstrated

Edited by kylomas

Forum Rules         Procedure for posting code

"I like pigs.  Dogs look up to us.  Cats look down on us.  Pigs treat us as equals."

- Sir Winston Churchill

Link to comment
Share on other sites

Run this little test and you will see that variables are not shared unless declared as static

Test()
func Test()
Local $a
Static Local $b
 $a += 1
 ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $a = ' & $a & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
 $b += 1
 ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $b = ' & $b & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
if $b > 1000 Then return
 Test()
EndFunc

"Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to build bigger and better idiots. So far, the universe is winning."- Rick Cook

Link to comment
Share on other sites

If you run this in Scite, you will see that in the first function $Test gets reset after every call to the Test function. In the second function, because I'm using the Static command, $Test doesn't get reset.

Local $Count = 1
Test()
$Count = 1
Test2()
Func Test()
    Local $Test=1
    ConsoleWrite("Running function Test - $Test = " & $Test & @CRLF)
    $Count += 1
    If $Count = 10 Then Return
    $Test += 1
    Test()
EndFunc

Func Test2()
    Local Static $Test=1
    ConsoleWrite("Running function Test2 - $Test = " & $Test & @CRLF)
    $Count += 1
    If $Count = 10 Then Return
    $Test += 1
    Test2()
EndFunc

If you mean something different, please explain.

EDIT: Bowmore beat me to it.

Edited by BrewManNH

If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Link to comment
Share on other sites

The behaviour as described is what I would expect, however it is not what I am seeing.

I have a group of documents (inside an application that I'm automating), I step through each document in the group, and perform some tasks, however every so often there is a sub group, I call the same function, but point it to the subgroup. However when it returns from the subgroup the position in the main group has changed to the last position in the sub. I have step through it in the debugger to confirm this is the case. And it is defintitely not set as static.

Link to comment
Share on other sites

  • Moderators

drum,

As I said above, please post the code you used - or a short reproducer. Otherwise we are just whistling in the dark. :whistle:

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to comment
Share on other sites

drum,

As I said above, please post the code you used - or a short reproducer. Otherwise we are just whistling in the dark. :whistle:

M23

This is the function in question - Not very easy to test but incase you spot an idiot mistake I'll post it. If not I'll try and reproduce the fault in a smaller code later.

Func ProcessGroup($Type)
Global $root, $sSeries, $sModel, $sYear, $sDocType, $sMainGroup, $sSubGroup, $dtnum

$sMainGroup = _ALLTRIM(ControlCommand("TIS", "", "ListBox16", "GetCurrentSelection"))

if $Type = "Sub" Then
$listbox = "ListBox17"
$sSubGroup = _ALLTRIM(ControlCommand("TIS", "", "ListBox17", "GetCurrentSelection"))
$group = $sSubGroup
else
$listbox = "ListBox16"
$group = $sMainGroup
EndIf


$grpnum = ControlCommand("TIS", "", $listbox, "FindString", $group)
if $grpnum = 0 Then
$grpnum = ControlCommand("TIS", "", $listbox, "FindString", $group&" ")
EndIf
While $grpnum < _GUICtrlListBox_GetCount(ControlGetHandle("TIS","",$listbox))
if ($Type <> "Sub") And (ControlCommand("TIS", "", "ListBox17", "IsVisible")) Then
ControlCommand("TIS", "", "ListBox17", "SetCurrentSelection", 0)
Back()
Sleep(500)
$mngrp = $grpnum
ProcessGroup("SUB")
$grpnum = $mngrp
Else
Forward();
Sleep(500)
if ControlCommand("TIS", "", "EtkTable3", "IsVisible") Then
ProcessDocTable()
Back()
Else
ProcessDoc(1)
Back()
EndIf
EndIf
$grpnum = $grpnum + 1
If $grpnum < _GUICtrlListBox_GetCount(ControlGetHandle("TIS","",$listbox)) Then
ControlCommand("TIS", "", $listbox, "SetCurrentSelection", $grpnum)
Back()
Sleep(500)
$group = _ALLTRIM(ControlCommand("TIS", "", $listbox, "GetCurrentSelection"))
if $Type = "Sub" Then
$sSubGroup = $group
else
$sMainGroup = $group
$sSubGroup = ""
EndIf
EndIf
Wend
If $Type = "Sub" Then
$sSubGroup = ""
EndIf
EndFunc
Link to comment
Share on other sites

  • Moderators

drum,

The first thing that jumps out at me is this line:

Global $root, $sSeries, $sModel, $sYear, $sDocType, $sMainGroup, $sSubGroup, $dtnum

If you declare the $sMainGroup and $sSubGroup variables as Global it is hardly surprising that you carry over the values between recursive calls. ;)

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to comment
Share on other sites

drum,

The first thing that jumps out at me is this line:

Global $root, $sSeries, $sModel, $sYear, $sDocType, $sMainGroup, $sSubGroup, $dtnum

If you declare the $sMainGroup and $sSubGroup variables as Global it is hardly surprising that you carry over the values between recursive calls. ;)

M23

The function is deisned to handle those variables, as it steps through things and can then se parent groups etc. the problem variable is $grpnum
Link to comment
Share on other sites

  • Moderators

drum,

Without the external app and the rest of the script so the code actually runs, it is almost impossible to debug that function as we have no real idea what is going on inside it - particularly as ther are no commnets or hints as to the code meaning. All I can say is that declaring variables as Global inside a function is bad coding practice and could (will) lead to all sorts of problems if the function is recursive. ;)

In any event I would strongly suggest that if you continue to have problems you recast the script to be iterative rather then recursive - much as shown in the tutorial to which I linked above. :)

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to comment
Share on other sites

One thing I did notice is in the examples variables are often declared as "LOCAL" where as I have just used them assuming they would default to LOCAL, I'm wondering if maybe this isn't too reliable.

I did say the code wouldn't be easy to follow, I've got to step away from the keyboard shortly, but when I return I'll try redfining the offending variable local, to see if changes anything.

Link to comment
Share on other sites

  • Moderators

drum,

AutoIt automatically scopes any variables in the main script as Global and any within Func...EndFunc definitions as Local - although we normally suggest that variables are explicitly scoped. I have not noticed any tendancy for this automatic scoping to be unreliable - and I have no reason to suppose that it is not. I fear you are clutching at straws with that idea. :)

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to comment
Share on other sites

drum,

Don't "poke and hope" that's just going to confuse the issue. Make a reproducer that will demonstrate the problem. This often presents the solution tothe problem as it forces you to r-visit the code in detail, but from a different perspective.

With regard to scope. Look at it like this, if the variable is defined outside of a function then it's scope is "global", even though you used the "local" keyword (because it is "local" to the script or file as guinness calls it). If you define a variable within a function using the "local" keyword then the variable only exists for the duration of that function and the scope is local to that function. Obviously, if you define a variable using the "global" keyword then it's scope is "global" regardless of where it is defined.

Hope this helps!

kylomas

Edited by kylomas

Forum Rules         Procedure for posting code

"I like pigs.  Dogs look up to us.  Cats look down on us.  Pigs treat us as equals."

- Sir Winston Churchill

Link to comment
Share on other sites

I'm guessing you mean that after running the function recursively, the code returns to the the following line $grpnum = $mngrp. I am imagining this isn't where you want it to be.

$mngrp = $grpnum
ProcessGroup("SUB")
$grpnum = $mngrp

These three lines of code make no sense to me. I don't see how ProcessGroup() modifies $mngrp unless it happens to be a global. There are no global variiables in the code you posted.

Edited by czardas
Link to comment
Share on other sites

always use local to define the variables in a function or you will have troubles in the long run, for example for $i loops with undefined scope defaults to global for $i so 2 functions using a for $i loop will interfere with each other (if the seconds function is called in the loop of the first)

as for the the code, you never declared your variables so they are all global.

I am curious about the working example

Local $Count = 1
Test()
$Count = 1
Test2()
Func Test()
Local $Test=1
ConsoleWrite("Running function Test - $Test = " & $Test & @CRLF)
$Count += 1
If $Count = 10 Then Return
$Test += 1
Test()
EndFunc

Func Test2()
Local Static $Test=1
ConsoleWrite("Running function Test2 - $Test = " & $Test & @CRLF)
$Count += 1
If $Count = 10 Then Return
$Test += 1
Test2()
EndFunc

Why can Test and Test2 see $Count... $Count is local...

Edited by icuurd12b42
Link to comment
Share on other sites

icuurd12b42,

Because it's scope is "global". Read my previous post for why this is.

kylomas

edit: additional info

Using the same control variable for nested loops is a problem because both loops are updating the same variable, not because of scope.

Edited by kylomas

Forum Rules         Procedure for posting code

"I like pigs.  Dogs look up to us.  Cats look down on us.  Pigs treat us as equals."

- Sir Winston Churchill

Link to comment
Share on other sites

@icuurd12b42

Actually For creates a local variable.

The Variable will be created automatically with a LOCAL scope, even when MustDeclareVars is on.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...