Jump to content

Recursion


NickT
 Share

Recommended Posts

I am new to AutoIt and could use some help with a script that I am writing. The problem that I keep getting is a recursion error. Here is the script:

#NoTrayIcon

Global $count=0

startup()

Func startup()

$count=$count+1

DriveMapDel("z:")

Sleep(2000)

If $count=7 Then Shutdown(22)

ShellExecuteWait("startup.bat", "", "c:\windows\system", "", @SW_HIDE)

DriveStatus("z:")

If @error Then startup()

$read=FileRead("c:\log\log.txt")

FileWrite("z:\log.txt", $read & @CRLF)

FileDelete("c:\log\log.txt")

$count=0

pingtest()

EndFunc

Func pingtest()

If $count=7 Then

ShellExecuteWait("nopinglog.bat", "", "c:\windows\system", "", @SW_HIDE)

ProcessClose("specified process")

ProcessClose("specified process")

ProcessClose("specified process")

$count=0

Sleep(5000)

startup()

EndIf

Ping("10.0.0.1", 15000)

If @error Then

$count=$count+1

Sleep(5000)

pingtest()

Else

$count=0

Sleep(55000)

time()

EndIf

EndFunc

Func time()

$time=@HOUR & ":" & @MIN & ":" & @SEC

If $time >= "20:00:00" And $time <= "20:01:00" Then

ShellExecuteWait("pinglog.bat", "", "c:\windows\system", "", @SW_HIDE)

Sleep(60000)

runcheck()

Else

runcheck()

EndIf

EndFunc

Func runcheck()

ProcessExists("specified process")

If @error Then

ShellExecute("specified program", "", "Program location")

Sleep(10000)

pingtest()

Else

pingtest()

EndIf

EndFunc

Any help would be greatly appreciated.

Edited by NickT
Link to comment
Share on other sites

Your problem is that all of your functions are either calling themself or each other, and those functions call back to the ones that started them. You need to seperate your error checking from your functions, and NEVER call a function from within itself. Use Returns instead of calling back to Startup. You need to have an actual script/program calling functions that do things, and then return results from doing those things. Right now, all you're doing is looping back and forth between your functions and running out of stack space.

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

Your problem is that all of your functions are either calling themself or each other, and those functions call back to the ones that started them. You need to seperate your error checking from your functions, and NEVER call a function from within itself. Use Returns instead of calling back to Startup. You need to have an actual script/program calling functions that do things, and then return results from doing those things. Right now, all you're doing is looping back and forth between your functions and running out of stack space.

So if I understand correctly, I need to write another script that "includes" this script and make this script my function script?
Link to comment
Share on other sites

  • Moderators

NickT,

Not quite. :) You need to have a small central core of your script to which you return every now and again so that the system "unwinds" from function calls.

What you are essentially doing in your current script is this:

; This code will fail - it is an example of recursion limits being exceeded
Global $iIndex = 1

_Print_Me()

Func _Print_Me()

    ConsoleWrite($iIndex & @CRLF)
    $iIndex += 1

    _Print_Me() ; call function again from within itself

EndFunc

You see that it fails because you are continually calling the function from within itself. Recursion is something most coders avoid like the plague - or use very carefully if they have to use it.

What you need to do is something like this:

Global $iIndex = 1

While 1

    _Print_Me() ; call function from within the idle loop

    $iIndex += 1
    If $iIndex = 5000 Then Exit

WEnd

Func _Print_Me()

    ConsoleWrite($iIndex & @CRLF)
    ; The function now ends and returns to the idle loop

EndFunc

I hope you can see the difference - in the second version your function ends each time and you return to the While...WEnd loop before calling the function again. You could set the $iIndex value to anything you wanted (within AutoIt's numerical limits, of course) and you would not run into problems.

You need a similar "idle" loop in your script to which you return on occasion to let the system "unwind". You can, of course, call several functions (even several times) before returning, but avoid calling any of them from within themselves.

Does that make it clearer? If not, please ask again. ;)

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

NickT,

Not quite. :) You need to have a small central core of your script to which you return every now and again so that the system "unwinds" from function calls.

What you are essentially doing in your current script is this:

; This code will fail - it is an example of recursion limits being exceeded
Global $iIndex = 1

_Print_Me()

Func _Print_Me()

    ConsoleWrite($iIndex & @CRLF)
    $iIndex += 1

    _Print_Me() ; call function again from within itself

EndFunc

You see that it fails because you are continually calling the function from within itself. Recursion is something most coders avoid like the plague - or use very carefully if they have to use it.

What you need to do is something like this:

Global $iIndex = 1

While 1

    _Print_Me() ; call function from within the idle loop

    $iIndex += 1
    If $iIndex = 5000 Then Exit

WEnd

Func _Print_Me()

    ConsoleWrite($iIndex & @CRLF)
    ; The function now ends and returns to the idle loop

EndFunc

I hope you can see the difference - in the second version your function ends each time and you return to the While...WEnd loop before calling the function again. You could set the $iIndex value to anything you wanted (within AutoIt's numerical limits, of course) and you would not run into problems.

You need a similar "idle" loop in your script to which you return on occasion to let the system "unwind". You can, of course, call several functions (even several times) before returning, but avoid calling any of them from within themselves.

Does that make it clearer? If not, please ask again. ;)

M23

I think I know what to do now. Thanks Melba. I do have a couple of questions though. From what I understand, the script returns to the beginning on its own, correct? And the While loop goes through the entire process even after going to the _Print_Me() Function? Edited by NickT
Link to comment
Share on other sites

  • Moderators

NickT,

First, when you reply please use the "Add Reply" button at the top and bottom of the page rather then the "Reply" button in the post itself. That way you do not get the contents of the previous post quoted in your reply and the whole thread becomes easier to read. ;)

the script returns to the beginning on its own, correct?

When a function ends it returns to the calling code and runs the next line. Technically there is a Return function, but unless you want to return a specific value, most coders ignore it.

And the While loop goes through the entire process even after going to the _Print_Me() Function?

The While loop continues with each line until it finds a WEnd at which point it restarts with the first line after the While - assuming the While condition is not met, of course. ;)

As you are obviously new to all of this (which is not a problem as we all started there!) might I suggest the excellent tutorials that you will find here and here - you will find other tutorials in the Wiki (the link is at the top of the page). You need to get a good grasp of functions and loops if you want to code anything more than the simplest of scripts.

All clear now? :)

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

Basically you still need a way to exit the program but I played around with your original code and kept it as intact as possible only changing the program flow to avoid stack buildup.

It will loop forever and run startup(). startup() will do some things then loop forever doing 3 stages of pingtest(), time(), and runcheck(). Since time() and runcheck() didn't seem to have any conditional changes to the flow I just made them 1 time calls after pingtest(). pingtest() will return true or falls on success. I'm not sure why you have all the long sleeps in there but I left them. It will retry 7 times before causing the original startup() to return false at which point your function stack is clear and startup() gets called again because it is in a loop itself. Not sure if this is how you wanted it but I tried to make as few changes as possible. I would suggest not looping forever I just did that since your original code had no end as well.

#NoTrayIcon

Global $count = 0

While True
    startup()
WEnd

Func startup()
    $count = $count + 1
    DriveMapDel("z:")
    Sleep(2000)
    If $count = 7 Then Shutdown(22)

    ShellExecuteWait("startup.bat", "", "c:\windows\system", "", @SW_HIDE)

    DriveStatus("z:")

    If @error Then startup()

    $read = FileRead("c:\log\log.txt")

    FileWrite("z:\log.txt", $read & @CRLF)

    FileDelete("c:\log\log.txt")

    While True
        $count = 0
        While Not pingtest()
            If $count = 7 Then
                ShellExecuteWait("nopinglog.bat", "", "c:\windows\system", "", @SW_HIDE)
                ProcessClose("specified process")
                ProcessClose("specified process")
                ProcessClose("specified process")
                Sleep(5000)
                Return False
            EndIf
        WEnd
        time()
        runcheck()
    WEnd

EndFunc   ;==>startup

Func pingtest()
    Ping("10.0.0.1", 15000)
    If @error Then
        $count = $count + 1
        Sleep(5000)
        Return False
    Else
        $count = 0
        Sleep(55000)
        Return True
    EndIf
EndFunc   ;==>pingtest

Func time()
    $time = @HOUR & ":" & @MIN & ":" & @SEC
    If $time >= "20:00:00" And $time <= "20:01:00" Then
        ShellExecuteWait("pinglog.bat", "", "c:\windows\system", "", @SW_HIDE)
        Sleep(60000)
    EndIf
EndFunc   ;==>time

Func runcheck()
    ProcessExists("specified process")
    If @error Then
        ShellExecute("specified program", "", "Program location")
        Sleep(10000)
    EndIf
EndFunc   ;==>runcheck
Edited by ShawnW
Link to comment
Share on other sites

ShawnW and M23, thank you for your help. Because of your explanations I believe I was able to figure out what I wanted to do. Here is an updated post of the script. If you have anymore suggestions, please don't hesitate. Again, thank you.

#NoTrayIcon

Global $count=0

While True

start()

drive()

WEnd

Func start()

$count=$count+1

If $count=7 Then Shutdown(22)

ProcessClose("")

ProcessClose("")

ProcessClose("")

DriveMapDel("z:")

ShellExecuteWait("")

EndFunc

Func drive()

DriveStatus("z:")

If Not @error Then

$read=FileRead("c:\log\log.txt")

FileWrite("z:\log.txt", $read & @CRLF)

FileDelete("c:\log\log.txt")

$count=0

Else

Return False

EndIf

While 1

$d=DriveMapGet("z:")

If Not $d Then Return False

$count=0

While Not s_ping()

If $count=7 Then

ShellExecuteWait("")

ProcessClose("")

ProcessClose("")

ProcessClose("")

$count=0

Return False

EndIf

WEnd

WEnd

EndFunc

Func s_ping()

Ping("10.0.0.1", 15000)

If @error Then

$count=$count+1

Else

$count=0

time()

p_run()

Return False

EndIf

EndFunc

Func time()

$time=@HOUR & ":" & @MIN & ":" & @SEC

If $time >= "20:00:00" And $time <= "20:01:00" Then

ShellExecuteWait("")

EndIf

EndFunc

Func p_run()

$p=ProcessExists("")

If Not $p Then ShellExecute("")

EndFunc

Link to comment
Share on other sites

mate, please use code tags, it makes it MUCH more readable. Also look at Tidy that comes with Scite.

#NoTrayIcon

Global $count = 0

While True
    start()
    drive()
WEnd

Func start()
    $count = $count + 1
    If $count = 7 Then Shutdown(22)
    ProcessClose("")
    ProcessClose("")
    ProcessClose("")
    DriveMapDel("z:")
    ShellExecuteWait("")
EndFunc   ;==>start

Func drive()
    DriveStatus("z:")
    If Not @error Then
        $read = FileRead("c:\log\log.txt")
        FileWrite("z:\log.txt", $read & @CRLF)
        FileDelete("c:\log\log.txt")
        $count = 0
    Else
        Return False
    EndIf
    While 1
        $d = DriveMapGet("z:")
        If Not $d Then Return False
        $count = 0
        While Not s_ping()
            If $count = 7 Then
                ShellExecuteWait("")
                ProcessClose("")
                ProcessClose("")
                ProcessClose("")
                $count = 0
                Return False
            EndIf
        WEnd
    WEnd
EndFunc   ;==>drive

Func s_ping()
    Ping("10.0.0.1", 15000)
    If @error Then
        $count = $count + 1
    Else
        $count = 0
        time()
        p_run()
        Return False
    EndIf
EndFunc   ;==>s_ping

Func time()
    $time = @HOUR & ":" & @MIN & ":" & @SEC
    If $time >= "20:00:00" And $time <= "20:01:00" Then
        ShellExecuteWait("")
    EndIf
EndFunc   ;==>time


Func p_run()
    $p = ProcessExists("")
    If Not $p Then ShellExecute("")
EndFunc   ;==>p_run

Post your code because code says more then your words can. SciTe Debug mode - it's magic: #AutoIt3Wrapper_run_debug_mode=Y. Use Opt("MustDeclareVars", 1)[topic="84960"]Brett F's Learning To Script with AutoIt V3[/topic][topic="21048"]Valuater's AutoIt 1-2-3, Class... is now in Session[/topic]Contribution: [topic="87994"]Get SVN Rev Number[/topic], [topic="93527"]Control Handle under mouse[/topic], [topic="91966"]A Presentation using AutoIt[/topic], [topic="112756"]Log ConsoleWrite output in Scite[/topic]

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...