Nutster Posted October 16, 2009 Share Posted October 16, 2009 (edited) I am still working on this, but life has an unfortunate habit of getting in the way. I think I have finished coding (this time!) and am running test scripts. There have been a couple of false starts and rewrites when Valik or I thought of a limitation in the implementation, but I think we have everything worked out. I hope to finish my testing this weekend and submit. Oh, yeah, I have to write some example code for the docs. Any suggestions? My test scripts would be more confusing than enlightening. Edited October 16, 2009 by Nutster Formatting for clarity & added last line. David NuttallNuttall Computer Consulting An Aquarius born during the Age of Aquarius AutoIt allows me to re-invent the wheel so much faster. I'm off to write a wizard, a wonderful wizard of odd... Link to comment Share on other sites More sharing options...
Authenticity Posted October 16, 2009 Share Posted October 16, 2009 I think that it can be a nice opportunity to snap in a recursive function example. It can be real great to remove a global variable usage or a wrapper function and very demonstrative. Don't have a specific example though. Link to comment Share on other sites More sharing options...
Valik Posted October 16, 2009 Share Posted October 16, 2009 There is never a nice opportunity to demonstrate recursion. Link to comment Share on other sites More sharing options...
monoceres Posted October 16, 2009 Share Posted October 16, 2009 Valik, I suspect you're a big Lisp fan. Broken link? PM me and I'll send you the file! Link to comment Share on other sites More sharing options...
Nutster Posted October 16, 2009 Share Posted October 16, 2009 I think that it can be a nice opportunity to snap in a recursive function example. It can be real great to remove a global variable usage or a wrapper function and very demonstrative. Don't have a specific example though. I like recursion exits to be based on the data that the recursive function is manipulating, not on the fact that you have run-away recursion and are about to run out of memory! Keep in mind that many uses of recursion are way more efficient using loops instead. You really only need recursion when you need to store more than one set of state information at a time. Walking binary (and more) trees and quick sort are two types of functions where recursion is a good thing. Fibonacci series and factorial are bad examples of recursion; they are each incredibly more efficient using loops instead of recursion, especially with large numbers.Recursion: See RecursionRecursion, Two-Layer: See Two-Layer RecursionTwo-Layer Recursion: See Recursion, Two-LayerOne problem with these definitions is there is no suitable exit condition. Run-away recursion! David NuttallNuttall Computer Consulting An Aquarius born during the Age of Aquarius AutoIt allows me to re-invent the wheel so much faster. I'm off to write a wizard, a wonderful wizard of odd... Link to comment Share on other sites More sharing options...
Valik Posted October 16, 2009 Share Posted October 16, 2009 The last time I wrote something that used recursion I felt dirty afterwards. I'm trying to remember what it was that I wrote. It was either written in AutoIt or C++ for AutoIt. Link to comment Share on other sites More sharing options...
Authenticity Posted October 16, 2009 Share Posted October 16, 2009 Perhaps. I think that Knight's Tour is something interesting. Sometimes, as you said recursion is the only sane way, other than solving 2^64 cases using loops. Link to comment Share on other sites More sharing options...
Nutster Posted October 16, 2009 Share Posted October 16, 2009 (edited) Because of the state information that is needed between passes, solving a knights tour using recursion is pretty good. Do it correctly and you can keep the memory use down this way as well. expandcollapse popupFunc WalkNext($Board, $new_row, $new_col, $Move=1) ; I do want the array to copy here. If UBound($Board,0) = 2 AND UBound($Board, 1) = 8 AND UBound($Board, 2) = 8 Then ; Make sure that the given co-ordinates are on the board. Switch $new_row Case 0 To 7 Switch $new_col Case 0 To 7 ; Check if the new position is already used. If $Board[$new_row][$new_col] > 0 Then ; Already played. Return false Else ; Ok, so the knight moved here. $Board[$new_row][$new_col] = $Move EndIf Case Else Return False EndSwitch Case Else Return False EndSwitch Else ; If you trigger this, you are not using the correct size board. Return False Endif ; If you get here, the data has been validated. Local $tmp_row, $tmp_col ; Make sure the board is not full. For $tmp_row = 0 to 7 For $tmp_col = 0 to 7 If $Board[$tmp_row][$tmp_col] = 0 Then ; There is an empty spot ExitLoop 2 Endif Next Next If $tmp_row > 7 Then ; Array is full! We have done it! Return True EndIf $Move += 1 ; All sorts of recursion, one at a time. If WalkNext($Board, $new_row+2, $new_col+1, $Move) Then Return True If WalkNext($Board, $tmp_row+2, $tmp_col-1, $Move) Then Return True If WalkNext($Board, $new_row-2, $new_col+1, $Move) Then Return True If WalkNext($Board, $tmp_row-2, $tmp_col-1, $Move) Then Return True If WalkNext($Board, $new_row+1, $new_col+2, $Move) Then Return True If WalkNext($Board, $tmp_row+1, $tmp_col-2, $Move) Then Return True If WalkNext($Board, $new_row-1, $new_col+2, $Move) Then Return True If WalkNext($Board, $tmp_row-1, $tmp_col-2, $Move) Then Return True Return False EndFunc Global $Pos1, $Pos2 Global $Board[8][8] For $Pos1 = 0 to 7 For $Pos2 = 0 to 7 If WalkNext($Board, $Pos1, $Pos2) Then Msgbox("Array walked! Started at (" & $Pos1 & ", " & $Pos2 & ")") ; You should probably do something to display the array. EndIf Next Next Too bad there is not a good use of Static in this. Edited October 16, 2009 by Nutster David NuttallNuttall Computer Consulting An Aquarius born during the Age of Aquarius AutoIt allows me to re-invent the wheel so much faster. I'm off to write a wizard, a wonderful wizard of odd... Link to comment Share on other sites More sharing options...
Authenticity Posted October 16, 2009 Share Posted October 16, 2009 Heh Nutster, a little imagination (mine is flawed + the code is not my revision at all) and you can see two statics. You're welcome to disagree about their usefulness. I don't know how recursion is not totally related to statics but I guess it was me too rushy. Anyway, refer only to the part you can dismiss $iCur and $iMax globally into statically for that matter. Your opinion about their relevance is welcome. expandcollapse popup#include <GUIConstantsEx.au3> #include <StaticConstants.au3> Opt("GUIOnEventMode", 1) Global Const $iChessBoardColor = 0x3EBBF1 Global Const $iChessSquareBKColor = 0xB1E7FA, $iChessSquareColor = 0xFFFFFF Global $hGUI Global $avChess[8][8], $avLabels[8][8] Global $iX, $iY Global $iCur, $iMax Global $fDone _Main() Func _Main() Local Const $iGUIHeight = Int(@DesktopHeight*0.8), $iGUIWidth = Int(@DesktopWidth*0.6) Local Const $iSquareHeight = Int($iGUIHeight/8), $iSquareWidth = Int($iGUIWidth/8) $hGUI = GUICreate("Chess Knight's Tour", $iGUIWidth, $iGUIHeight) GUISetBkColor($iChessBoardColor) GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit") For $i = 0 To 7 For $j = 0 To 7 $avLabels[$i][$j] = GUICtrlCreateLabel('', $j*$iSquareWidth+2, $i*$iSquareHeight+2, $iSquareWidth-2, $iSquareHeight-2, BitOR($SS_CENTER, $SS_CENTERIMAGE, $SS_SUNKEN)) GUICtrlSetFont(-1, 30, 500) GUICtrlSetBkColor(-1, $iChessSquareBKColor) GUICtrlSetColor(-1, $iChessSquareColor) Next Next GUISetState() Do _InitializeVars() _RedrawBoard() _Retour($iX, $iY) Until MsgBox(0x24, "Retour", "Would you like to do the tour again?") <> 6 GUIDelete() Exit EndFunc Func _Exit() GUIDelete() Exit EndFunc Func _CanMove($iX, $iY) If $iX < 0 Or $iX > 7 Or $iY < 0 Or $iY > 7 Or $avChess[$iX][$iY] Then Return False Else Return True EndIf EndFunc Func _Retour($iX, $iY) Sleep(0) If $iCur > $iMax Then $iMax = $iCur _RedrawBoard() Return EndIf If _CanMove($iX+1, $iY+2) Then $iCur += 1 $avChess[$iX+1][$iY+2] = $iCur If $iCur = 64 Then Return True If _Retour($iX+1, $iY+2) Then Return True Else $avChess[$iX+1][$iY+2] = "" $iCur -= 1 EndIf EndIf If _CanMove($iX+1, $iY-2) Then $iCur += 1 $avChess[$iX+1][$iY-2] = $iCur If $iCur = 64 Then Return True If _Retour($iX+1, $iY-2) Then Return True Else $avChess[$iX+1][$iY-2] = "" $iCur -= 1 EndIf EndIf If _CanMove($iX+2, $iY+1) Then $iCur += 1 $avChess[$iX+2][$iY+1] = $iCur If $iCur = 64 Then Return True If _Retour($iX+2, $iY+1) Then Return True Else $avChess[$iX+2][$iY+1] = "" $iCur -= 1 EndIf EndIf If _CanMove($iX+2, $iY-1) Then $iCur += 1 $avChess[$iX+2][$iY-1] = $iCur If $iCur = 64 Then Return True If _Retour($iX+2, $iY-1) Then Return True Else $avChess[$iX+2][$iY-1] = "" $iCur -= 1 EndIf EndIf If _CanMove($iX-1, $iY+2) Then $iCur += 1 $avChess[$iX-1][$iY+2] = $iCur If $iCur = 64 Then Return True If _Retour($iX-1, $iY+2) Then Return True Else $avChess[$iX-1][$iY+2] = "" $iCur -= 1 EndIf EndIf If _CanMove($iX-1, $iY-2) Then $iCur += 1 $avChess[$iX-1][$iY-2] = $iCur If $iCur = 64 Then Return True If _Retour($iX-1, $iY-2) Then Return True Else $avChess[$iX-1][$iY-2] = "" $iCur -= 1 EndIf EndIf If _CanMove($iX-2, $iY+1) Then $iCur += 1 $avChess[$iX-2][$iY+1] = $iCur If $iCur = 64 Then Return True If _Retour($iX-2, $iY+1) Then Return True Else $avChess[$iX-2][$iY+1] = "" $iCur -= 1 EndIf EndIf If _CanMove($iX-2, $iY-1) Then $iCur += 1 $avChess[$iX-2][$iY-1] = $iCur If $iCur = 64 Then Return True If _Retour($iX-2, $iY-1) Then Return True Else $avChess[$iX-2][$iY-1] = "" $iCur -= 1 EndIf EndIf Return False EndFunc Func _InitializeVars() $iCur = 0 $iMax = 0 ;~ $iX = Random(0, 7, 1) ;~ $iY = Random(0, 7, 1) $iX = 4 $iY = 5 $fDone = False For $i = 0 To 7 For $j = 0 To 7 $avChess[$i][$j] = "" Next Next WinSetTitle($hGUI, "", "Chess Knight's Tour") EndFunc Func _RedrawBoard() WinSetTitle($hGUI, "", "Chess Knight's Tour: Recusiong level (" & $iCur & ")") For $i = 0 To 7 For $j = 0 To 7 GUICtrlSetData($avLabels[$i][$j], $avChess[$i][$j]) Next Next EndFunc By the way, I guess that 24x24 board will take both approaches about a decent hour+ on an average machine. Link to comment Share on other sites More sharing options...
Valik Posted October 16, 2009 Share Posted October 16, 2009 I don't know how recursion is not totally related to statics but I guess it was me too rushy.Re-entrance can be caused by more than just recursion. A callback function may be called many times but will never be called recursively. Any situation where a function has static data that may or may not be initialized is a good place to use Static to get load-on-demand type behavior. For example, you might not wish to initialize a constant 10,000,000 element array as a global variable if there's a possibility that the array will never be used (maybe the program has a fatal error). By using Static to initialize the data on-demand the first time it's used the performance penalty for initializing the array is only incurred if the array is actually used.For me, static variables are synonymous with "load-on-demand" and "hyper-localized constant with runtime initialization" rather than recursion. The AutoIt source contains a few places where I've used static for either of those reasons. Ironically, I don't think static is used for recursion despite the fact that AutoIt is implemented using recursion. Link to comment Share on other sites More sharing options...
ProgAndy Posted October 16, 2009 Share Posted October 16, 2009 (edited) Maybe a static varibale would be useful in those UDF-Functions where the same DLLStruct is created for each call to it. Multiple calls to them would be faster if the struct was already existing Also it could be used to replace global variables wich are not constant, so you can't accidentally overwrite them. //Edit: e.g. opening DLLs in UDFs. At the moment you do it this way: Global $ghDLLFile = -1 func _LoadFunc() EndFunc You could move to: func _MYDLL($sDLL='my.dll', $bUnload=false) Static $hDLL=-1 Switch $bUnload Case True DLLClose($hDLL) $hDLL=-1 Case False If $hDLL<>-1 Then Return $hDLL $hDLL = DLLOpen($sDLL) REturn $hDLL EndSwitch EndFunc Edited October 16, 2009 by ProgAndy *GERMAN* [note: you are not allowed to remove author / modified info from my UDFs]My UDFs:[_SetImageBinaryToCtrl] [_TaskDialog] [AutoItObject] [Animated GIF (GDI+)] [ClipPut for Image] [FreeImage] [GDI32 UDFs] [GDIPlus Progressbar] [Hotkey-Selector] [Multiline Inputbox] [MySQL without ODBC] [RichEdit UDFs] [SpeechAPI Example] [WinHTTP]UDFs included in AutoIt: FTP_Ex (as FTPEx), _WinAPI_SetLayeredWindowAttributes Link to comment Share on other sites More sharing options...
Authenticity Posted October 16, 2009 Share Posted October 16, 2009 (edited) I believe that dll handles like the one in GDIPlus.au3 can be left as they are. If you're using the library which not all it's function are implemented in AutoIt UDF you can use the global handle instead of increasing it's counter and preforming a function call. But it's scopability versus usability (I guess). I can agree with Valik which is more realistic about what statics are.think I find interesting in C# is the reaonly keyword which allows you to declare constant field at runtime. I guess that Global Const $iScreenHeight = @DesktopHeight resolves to something like mov eax, 300 after script is (on the fly) compiled?Edit: My bad, AutoIt is an interpreted language. :S Edited October 16, 2009 by Authenticity Link to comment Share on other sites More sharing options...
Valik Posted October 16, 2009 Share Posted October 16, 2009 ProgAndy, those are bad examples of Static. The other side of Static is once it exists it always exists and never goes out of scope and no resource are ever released. Things that are static need to be used a lot to justify the memory residency. Link to comment Share on other sites More sharing options...
Bowmore Posted October 16, 2009 Share Posted October 16, 2009 An situation where I often use static variables is when I need to read a load of parameter/options/data from a configuration file where the data will not change while the program is running. Using Static variables means that the variables only have to be initialises the first time the function is called. expandcollapse popupFunc _Myfunc($test) Static $Var1 = IniRead(@ScriptDir & "\myfile.ini", "section", "rule1", "NotFound") Static $Var2 = IniRead(@ScriptDir & "\myfile.ini", "section", "rule2", "NotFound") Static $Var3 = IniRead(@ScriptDir & "\myfile.ini", "section", "rule3", "NotFound") Static $Var4 = IniRead(@ScriptDir & "\myfile.ini", "section", "rule4", "NotFound") Static $Var5 = IniRead(@ScriptDir & "\myfile.ini", "section", "rule5", "NotFound") Static $Var6 = IniRead(@ScriptDir & "\myfile.ini", "section", "rule6", "NotFound") Static $Var7 = IniRead(@ScriptDir & "\myfile.ini", "section", "rule7", "NotFound") Static $Var8 = IniRead(@ScriptDir & "\myfile.ini", "section", "rule8", "NotFound") Static $Var9 = IniRead(@ScriptDir & "\myfile.ini", "section", "rule9", "NotFound") Static $Var10 = IniRead(@ScriptDir & "\myfile.ini", "section", "rule10", "NotFound") Switch $test Case $Var1 ;do something Case $Var2 ;do something Case $Var3 ;do something Case $Var4 ;do something Case $Var5 ;do something Case $Var6 ;do something Case $Var7 ;do something Case $Var8 ;do something Case $Var9 ;do something Case $Var11 EndSwitch Return 0 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 More sharing options...
Nutster Posted October 17, 2009 Share Posted October 17, 2009 I like that last example, Bowmore. I primary use statics when I need to retain state information between calls to a function, and do not want anyone else accidentally changing a global variable. I am not referring to recursion, but calling a function in a loop. Func Analyze($line) Static $state = 0, $lastline = "" Switch $state Case 0 $lastline = $line $state = 1 return _FirstWord($line) Case 1 If $line > $lastline Then ; Whatever EndIf ; Other cases End Switch EndFunc $file = FileOpen("Filename.txt") While Not EOF($file) $Text = FileRead($file) Analyze($Text) Wend FileClose($file) Yeah, I could use something like this for a simple example. David NuttallNuttall Computer Consulting An Aquarius born during the Age of Aquarius AutoIt allows me to re-invent the wheel so much faster. I'm off to write a wizard, a wonderful wizard of odd... Link to comment Share on other sites More sharing options...
Nutster Posted October 19, 2009 Share Posted October 19, 2009 Finally, Trac issue #508 (Static variables) is done! I apologize to everybody that it took so long, but other issues had to be given priority. There were a bunch of technical issues to get around, but it is done now and it should appear in the next beta (3.3.1.4). David NuttallNuttall Computer Consulting An Aquarius born during the Age of Aquarius AutoIt allows me to re-invent the wheel so much faster. I'm off to write a wizard, a wonderful wizard of odd... Link to comment Share on other sites More sharing options...
Greenhorn Posted October 25, 2009 Share Posted October 25, 2009 Thank you very much for that feature, Nutster. Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now