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 

Recursive function with ternary operator

 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Bug Reports
View previous topic :: View next topic  
Author Message
Laszlo



Joined: 14 Feb 2005
Posts: 4012
Location: Pittsburgh

PostPosted: Sat Mar 03, 2007 8:20 pm    Post subject: Recursive function with ternary operator Reply with quote

The two functions below should return the same value ("y"), but they don't.
Code:
MsgBox % f("xy") ; y
MsgBox % g("xy") ; x
f(x) {
   IfEqual x,, Return
   z := f(SubStr(x,2,1))
   Return z = "" ? SubStr(x,1,1) : z
}
g(x) {
   IfEqual x,, Return
   Return (z := g(SubStr(x,2,1))) = "" ? SubStr(x,1,1) : z
}
Back to top
View user's profile Send private message
jonny



Joined: 13 Nov 2004
Posts: 3004
Location: Minnesota

PostPosted: Sat Mar 03, 2007 8:38 pm    Post subject: Reply with quote

I'm not sure exactly why, but they both return the same value if z starts out non-blank:
Code:
z=5

MsgBox % f("xy")
MsgBox % g("xy")
f(x) {
global z
   If x =
      return
   z := f(SubStr(x,2,1))
   Return z = "" ? SubStr(x,1,1) : z
}
g(x) {
global z
   If x =
      return
   Return (z := g(SubStr(x,2,1))) = "" ? SubStr(x,1,1) : z
}
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4012
Location: Pittsburgh

PostPosted: Sat Mar 03, 2007 8:44 pm    Post subject: Reply with quote

It is important that z was local, because it gets changed in the recursive calls, and we need the value in the current level, not the last assigned value (global). However, jonny's observation shows that the bug can be related to the handling of local variables.
Back to top
View user's profile Send private message
jonny



Joined: 13 Nov 2004
Posts: 3004
Location: Minnesota

PostPosted: Sat Mar 03, 2007 8:50 pm    Post subject: Reply with quote

It shouldn't matter if z is being assigned after the recursive call, which it is in both versions. The problem could lie in the true/false expressions being evaluated before the conditional expression in the ternary operator, which would explain the first script working (where the assignment takes place before the whole expression), but not the second one.
Back to top
View user's profile Send private message
jonny



Joined: 13 Nov 2004
Posts: 3004
Location: Minnesota

PostPosted: Sat Mar 03, 2007 8:52 pm    Post subject: Reply with quote

Hmm, never mind, that can't be it:
Documentation wrote:
To enhance performance, only the winning branch is evaluated (see short-circuit evaluation).
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4012
Location: Pittsburgh

PostPosted: Sat Mar 03, 2007 9:05 pm    Post subject: Reply with quote

jonny wrote:
It shouldn't matter if z is being assigned after the recursive call
True. The example I posted was simplified until the problem remained. The bug cost me many hours of debugging in Monster, where there are more than one recursive calls, messing up z, if it was not local.
Back to top
View user's profile Send private message
Joy2DWorld



Joined: 04 Dec 2006
Posts: 422
Location: Galil, Israel

PostPosted: Sun Mar 04, 2007 3:54 am    Post subject: Reply with quote

well,


the issue is not strictly with the recursion,

it relates instead to the VALUATION of the ASSIGNMENT (:=)


eg

Code:
g(x) {
   IfEqual x,, Return ""
   z = ok
   Return ( (g(SubStr(x,2,1))) = "" ) ? SubStr(x,1,1) : z
}




works great.


ie.

Code:
g(x) {
   IfEqual x,, Return ""
   static v
   if !v
   v = 0
v++
tooltip % v " - " x
   z = ok
   
   Return ( (mx := g(SubStr(x,2,1))) = "" ) ? SubStr(x,1,1) : z
}



or, ie. the issue is in the evaluation of := and not with Ternary operators as such...


eg:

Code:
g(x) {
   IfEqual x,, Return ""
   static v
   if !v
   v = 0
v++
tooltip % v " - " x

   
   if  (z := g(SubStr(x,2,1))) = ""
   return SubStr(x,1,1)
else
   return  z
}



which produces the same x result and uses an IF, not a Ternary operator.


Quote:
If an assignment is used as the input for some other operator, its value is the variable itself. For example, the expression (Var+=2) > 50 is true if the newly-increased value in Var is greater than 50. This also allows an assignment to be passed ByRef, or its address taken; for example: &(x:="abc").




seems like is the breaking point... with the recursed function..




eg:

Code:
g(x) {
   IfEqual x,, Return ""
   static v
   if !v
   v = 0
v++
tooltip % v " - " x

   z := "w"
   if  (z := g(SubStr(x,2,1))) = ""
   return SubStr(x,1,1)
else
   return  z
}


breaks, whereas

Code:
g(x) {
   IfEqual x,, Return ""
   static v
   if !v
   v = 0
v++
tooltip % v " - " x

   
   if  (z := f(SubStr(x,2,1))) = ""
   return SubStr(x,1,1)
else
   return  z
}


works...


ie. Z is not being properly assigned from within an operator ( ) where Z is assigned from a recursed function...


hope this helps.


as can be seen here:

Code:
g(x) {
   IfEqual x,, Return ""
   static v
   if !v
   v = 0
v++
;tooltip % v " - " x

  fsee :=  (z :=  g(SubStr(x,2,1)))
   if  (z) = ""  {
   tooltip % z " is z"
   return SubStr(x,1,1)
} else
   return  z
}

_________________
Joyce Jamce
Back to top
View user's profile Send private message
Laszlo



Joined: 14 Feb 2005
Posts: 4012
Location: Pittsburgh

PostPosted: Sun Mar 04, 2007 6:26 am    Post subject: Reply with quote

I can confirm, Joy2DWorld is right. Thanks!

Here are some little shorter scripts for demo.

(1) The problem is not the ternary operator. The following, functionally equivalent code fails, too.
Code:
g(x) {
   IfEqual x,, Return
   If ((z := g(SubStr(x,2,1))) = "")
        Return SubStr(x,1,1)
   Else Return z
}
It is the same with automatic increase of precedence:
Code:
   If ("" = z := g(SubStr(x,2,1)))

The assignment inside an if (..) statement fails, regardless of its role (dummy):
Code:
g(x) {
   IfEqual x,, Return
   If (z := g(SubStr(x,2,1)))
       MsgBox NEVER
   Return z = "" ? SubStr(x,1,1) : z
}


(2) Assignments only to a recursive function fails inside an "if (..)" or ternary op. Assigning a local variable (y) works OK, like a non-recursive function:
Code:
g(x) {
   IfEqual x,, Return
   y := g(SubStr(x,2,1))
   Return (z :=  SubStr(y,1))) = "" ? SubStr(x,1,1) : z
}


The final values of the variables can be shown in the tray tip
Code:
g(x) {
   IfEqual x,, Return
   y := (z := g(SubStr(x,2,1))) = "" ? SubStr(x,1,1) : z
   TrayTip,,[%x%] - [%y%] - [%z%]
   Return y
}
TrayTip: [xy] – [x] – []

The final values when the function works:
Code:
g(x) {
   IfEqual x,, Return
   z := g(SubStr(x,2,1))
   y := z = "" ? SubStr(x,1,1) : z
   TrayTip,,[%x%] - [%y%] - [%z%]
   Return y
}
TrayTip: [xy] – [y] – [y]
Back to top
View user's profile Send private message
Chris
Site Admin


Joined: 02 Mar 2004
Posts: 10467

PostPosted: Sun Mar 04, 2007 10:30 pm    Post subject: Reply with quote

I believe this has been fixed in today's 1.0.46.09; but please let me know if you have any more problems with it.

This was actually broken by the following optimization in 1.0.46.06: "Improved performance when assigning large strings returned from user-defined functions." That optimization is still present but it no longer applies when the result of a recursive function-call is assigned to one of that function's own local variables.

Thanks for the detailed analysis; it made fixing this easier.
Back to top
View user's profile Send private message Send e-mail
Laszlo



Joined: 14 Feb 2005
Posts: 4012
Location: Pittsburgh

PostPosted: Sun Mar 04, 2007 11:35 pm    Post subject: Reply with quote

Yes, it is fixed in AHK 1.0.46.09. -Thanks, Chris!
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
Page 1 of 1

 
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