Jump to content

Usefulness of new feature: STATIC (local) variables decl


jchd
 Share

Recommended Posts

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 by Nutster
Formatting for clarity & added last line.

David Nuttall
Nuttall 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

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 Recursion

Recursion, Two-Layer: See Two-Layer Recursion

Two-Layer Recursion: See Recursion, Two-Layer

One problem with these definitions is there is no suitable exit condition. Run-away recursion!

David Nuttall
Nuttall 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

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.

Func 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 by Nutster

David Nuttall
Nuttall 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

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.

#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

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

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

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 by Authenticity
Link to comment
Share on other sites

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

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.

Func _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

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

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

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