AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Serious debug for StringSplit needed
Goto page 1, 2  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Bug Reports
View previous topic :: View next topic  
Author Message
guiKey



Joined: 25 Nov 2006
Posts: 7

PostPosted: Sat Feb 03, 2007 8:13 am    Post subject: Serious debug for StringSplit needed Reply with quote

Consider function f(x) containing a StringSplit command.
Briefly, calling f(x) from different points in the script, StringSplit gives different results. When StringSplit doesn't work, arrays are left blank.
Loop, Parse command solves the problem.

(WinXP SP2, AutoHotkey 1.0.46.07)


Last edited by guiKey on Sat Feb 03, 2007 10:08 am; edited 2 times in total
Back to top
View user's profile Send private message
SKAN



Joined: 26 Dec 2005
Posts: 5887

PostPosted: Sat Feb 03, 2007 9:46 am    Post subject: Re: Serious debug for StringSplit needed Reply with quote

guiKey wrote:
Briefly, calling f(x) from different points in the script, StringSplit gives different results. When StringSplit doesn't work, arrays are left blank.


If you use StringSplit inside a function, the elements of the pseudo-array will not be visible outside the function. To overcome this use global at the beginning of your function.


Code:
StringSplit( "Arr", "The Quick Brown Fox", A_Space )
MsgBox, %Arr1%`n%Arr2%`n%Arr3%`n%Arr4%

StringSplit( OutputArray, InputVar, Delimiters="", Omitchars="" ) {
Global
StringSplit, %OutputArray%, InputVar, %Delimiters%, %OmitChars%
Return (%OutputArray%0)
}


Smile
Back to top
View user's profile Send private message
guiKey



Joined: 25 Nov 2006
Posts: 7

PostPosted: Sat Feb 03, 2007 9:59 am    Post subject: Reply with quote

It's not about variables scope. Talking about array processed locally inside f(x). Unbelievable for me too, but ListVars in certain cases gives blank values. Have you ever seen that?
Back to top
View user's profile Send private message
SKAN



Joined: 26 Dec 2005
Posts: 5887

PostPosted: Sat Feb 03, 2007 10:51 am    Post subject: Reply with quote

Quote:
Unbelievable for me too, but ListVars in certain cases gives blank values. Have you ever seen that?


I have not used StringSplit much. It would help very much if you can post some code that can reproduce the effect.

Smile
Back to top
View user's profile Send private message
Chris
Site Admin


Joined: 02 Mar 2004
Posts: 10467

PostPosted: Sat Feb 03, 2007 2:39 pm    Post subject: Reply with quote

Yes, it would help to see an example. The behavior or local vs. global arrays is often a source of confusion.
Back to top
View user's profile Send private message Send e-mail
KZ



Joined: 06 Feb 2007
Posts: 4

PostPosted: Sat Mar 17, 2007 9:45 pm    Post subject: Re: Serious debug for StringSplit needed Reply with quote

guiKey wrote:
Consider function f(x) containing a StringSplit command.
Briefly, calling f(x) from different points in the script, StringSplit gives different results. When StringSplit doesn't work, arrays are left blank.
Loop, Parse command solves the problem.

(WinXP SP2, AutoHotkey 1.0.46.07)


Don't know if this is related to the original post, but I've just
spent a few hours debugging a similar (same?) problem.

Code:

; KZ_BUG001.AHK -- Reporducer for StringSplit bug

GetEnvInfo()        ; Works if called here
x := "var=123"
StringSplit,temp,x,=
MsgBox, %temp1% . %temp2%
; GetEnvInfo()        ; Does not if called here
return

; Functions
GetEnvInfo()        ; Get info
{
y := "var=456"
StringSplit,temp,y,=
MsgBox, %temp1% . %temp2%
return
}


I fixed the problem by using "junk" in the function (instead of "temp" twice), which shows I still have to learn about globals in AHK. I would really
like to use "temp" as I do in all other languages, ie, without worrying about the scope.
Back to top
View user's profile Send private message
jonny



Joined: 13 Nov 2004
Posts: 3004
Location: Minnesota

PostPosted: Sat Mar 17, 2007 11:16 pm    Post subject: Reply with quote

I think I've found the specific problem here. Hope you don't mind a lot of examples, but it's easier than explaining it. First, observe this script that works correctly:
Code:
f()
MsgBox main`n%temp1% %temp2%
return

f()
{
   x = ab
   StringSplit,temp,x
   MsgBox f()`n%temp1% %temp2%
   return
}

Function f()'s MsgBox will show 'a b' and the global MsgBox will show nothing. This is expected, since the array should be created locally in f(). Now, how about this script?
Code:
f()
MsgBox main`n%temp0% %temp1% %temp2%
return

f()
{
   x = ab
   StringSplit,temp,x
   MsgBox f()`n%temp1% %temp2%
   return
}

In this one, f()'s MsgBox shows nothing, but unexpectedly, the global MsgBox shows the result of the StringSplit! When temp0 is referenced at global scope, but not at function scope, the array seems to be created globally, even if it's referenced after the function call. Here's a script that corrects the problem:
Code:
f()
MsgBox main`n%temp0% %temp1% %temp2%
return

f()
{
   x = ab
   StringSplit,temp,x
   MsgBox f()`n%temp0% %temp1% %temp2%
   return
}

Once again, it works correctly, apparently because temp0 is referenced locally in function f() as well as globally.

This could be related to this part of the function documentation:
Documentation wrote:
All variables referenced or created inside a function are local by default (except built-in variables such as Clipboard, ErrorLevel, and A_TimeIdle).


However, the issue is clearly addressed later on in that same section:
Documentation wrote:
For commands that create arrays (such as StringSplit), the resulting array is local if the assume-global mode is not in effect or if the array's first element has been declared as a local variable (this is also true if one of the function's parameters is passed -- even if that parameter is ByRef -- because parameters are similar to local variables). Conversely, if the first element has been declared global, a global array is created.


Since assume-global mode has not been specified, and temp0 hasn't been declared as global, the array should be local. AHK gets confused when temp0 is referenced globally, though, unless I'm missing another part of the docs that allows for that case.

By the way, this is the same thing that's happening in KZ's script, too. It's harder to tell, but temp0 is created globally when he does the StringSplit in the main thread. It must be different between creating and referencing, because my example script doesn't work even though the call is before the reference. In addition, it seems that StringSplit creating temp0 inside the function doesn't count as creating it locally. So, to summarize:
  • When temp0 is created globally before the function call or referenced globally anywhere, StringSplit violates the documented behavior and creates its array globally when 'global' hasn't been specified and temp0 hasn't been declared with 'global temp0'.
Back to top
View user's profile Send private message
corrupt



Joined: 29 Dec 2004
Posts: 2397

PostPosted: Sun Mar 18, 2007 4:01 am    Post subject: Reply with quote

Unless I'm misunderstanding the issue, I had reported a similar bug previously and the conclusion was that using MsgBox for debugging variable values is useless since the variables in the MsgBox lines will be declared as globals if they are referenced anywhere that is not in a function. Even if the line containing the MsgBox command is never executed if I remember correctly (it's been a while though... I might be mistaken...)...

Edit: Hmm... maybe I was thinking of this topic...

Edit: Typo. I should use a spell checker...
Back to top
View user's profile Send private message Visit poster's website
jonny



Joined: 13 Nov 2004
Posts: 3004
Location: Minnesota

PostPosted: Sun Mar 18, 2007 6:34 am    Post subject: Reply with quote

Fair enough, but it can still be produced the way KZ demonstrated, and by explicitly setting the variable before the call:
Code:
temp0=1
f()
MsgBox main`n%temp1% %temp2%
return

f()
{
   x = ab
   StringSplit,temp,x
   MsgBox f()`n%temp1% %temp2%
   return
}
Back to top
View user's profile Send private message
jonny



Joined: 13 Nov 2004
Posts: 3004
Location: Minnesota

PostPosted: Sun Mar 18, 2007 7:04 am    Post subject: Reply with quote

Aha. I suppose I missed this part:
Documentation wrote:
Note: any non-dynamic reference to a variable creates that variable the moment the script launches. For example, a reference to Array1 outside a function creates Array1 as a global the moment the script launches. Conversely, a reference to Array1 inside a function immediately creates it as a local.


So, since StringSplit sees that temp0 is created, it makes the whole array global. It's documented behavior, and the solution is to reference or create temp0 inside the function.
Back to top
View user's profile Send private message
corrupt



Joined: 29 Dec 2004
Posts: 2397

PostPosted: Sun Mar 18, 2007 7:32 am    Post subject: Reply with quote

That does seem to be a bug since the function seems to update the global variables instead of updating the variables when they are modified in the function. I've guessed this by viewing the variable contents and lines last executed using the AHK window for the script when both MsgBoxes appear.
Back to top
View user's profile Send private message Visit poster's website
corrupt



Joined: 29 Dec 2004
Posts: 2397

PostPosted: Sun Mar 18, 2007 7:41 am    Post subject: Reply with quote

I understand what you are saying about the variables being considered global but the bug is the inconsistency. StringSplit and MsgBox inside the function should reference the same variables (unless I'm misunderstanding).

Edit: It's good that bugs are documented though Laughing ... I guess... Rolling Eyes

I would have expected that the StringSplit line should create a local variable from being referenced the same way that the MsgBox line seems to...
Back to top
View user's profile Send private message Visit poster's website
jonny



Joined: 13 Nov 2004
Posts: 3004
Location: Minnesota

PostPosted: Sun Mar 18, 2007 10:37 pm    Post subject: Reply with quote

Well, it's already clear that StringSplit creates a global array if temp0 is global, and a local array otherwise. That's certainly not a bug, it's expected and necessary behavior. The issue here is when temp0 is considered global.

In this case, since temp0 is taken as global, the array (including temp1 and temp2) are created globally, which is why StringSplit and MsgBox reference different variables.
Back to top
View user's profile Send private message
corrupt



Joined: 29 Dec 2004
Posts: 2397

PostPosted: Mon Mar 19, 2007 1:11 am    Post subject: Reply with quote

jonny wrote:
In this case, since temp0 is taken as global, the array (including temp1 and temp2) are created globally, which is why StringSplit and MsgBox reference different variables.
This is not logical though. The array in the StringSplit line is not a dynamic reference. Therefore it should create a local array the same way that the variables in MsgBox were created as locals. ...or the variable references in the MsgBox line should have referenced the global variables the same way the StringSplit line did. This is not consistent and it is a bad design to have 2 commands use 2 different methods.

The main reason I mentioned using MsgBox though is that it is the source of grief in this example also. The variables seem to be created as locals because they were referenced in the MsgBox line. Why is the StringSplit line immune to this but the MsgBox line isn't?

Here's an example to demonstrate:
Code:
temp0=1
f()
MsgBox main`n%temp1% %temp2%
return

f()
{
   x = ab
   t = temp
   out = f()`n
   StringSplit,temp,x
   MsgBox % out . %t%1 . %t%2
   return
}
Back to top
View user's profile Send private message Visit poster's website
jonny



Joined: 13 Nov 2004
Posts: 3004
Location: Minnesota

PostPosted: Mon Mar 19, 2007 2:24 am    Post subject: Reply with quote

corrupt wrote:
jonny wrote:
In this case, since temp0 is taken as global, the array (including temp1 and temp2) are created globally, which is why StringSplit and MsgBox reference different variables.
This is not logical though. The array in the StringSplit line is not a dynamic reference. Therefore it should create a local array the same way that the variables in MsgBox were created as locals. ...or the variable references in the MsgBox line should have referenced the global variables the same way the StringSplit line did. This is not consistent and it is a bad design to have 2 commands use 2 different methods.


What's your fixation with MsgBox? It isn't relevant to the issue at all, regardless of what problems it may have. The only issue here (and it's already documented, even if it isn't ideal behavior) is that if temp0 is already a variable in global scope, StringSplit inside a function will create the array globally. This means any reference in global scope will cause it to function that way. So, how is MsgBox concerned with this?

Just to make it even clearer, here's an example without MsgBox's. The two relevant lines are highlighted.
Code:
temp0=1
f()
ToolTip %temp1% %temp2%
Sleep 3000
return

f() {
   x = ab
   StringSplit,temp,x
   ToolTip %temp1% %temp2%
   Sleep 3000
   return
}

First tooltip shows nothing, second tooltip shows 'a b'. This proves that array 'temp' is created at global scope, due to temp0 being referenced. Remove the first line and the first tooltip outputs 'a b' with the second being blank.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Bug Reports All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group