Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Unexpected Result (left to right paramater passing)


  • Please log in to reply
61 replies to this topic
Xander
  • Members
  • 140 posts
  • Last active: Nov 13 2007 08:31 PM
  • Joined: 23 Sep 2007
Parameter values are not always passed from left to right, take this example:

List = 10,12
Index = 1
Loop, Parse, List, `,
	SurpriseMe(index, index += A_LoopField)
	
Index = 1
Loop, Parse, List, `,
	SurpriseMe(index-0, index += A_LoopField)


SurpriseMe(startIndex, endIndex) {
	MsgBox, %startIndex% %endIndex%
}

There are two loops that appear to be almost identical and (should be imo) identically semantically, but they are not!
In the first loop, you'll get a message box containing "11 11" followed by "23 23"

In the second loop, you get what you would expect, namely "1 11" followed by "11 23"

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005

Parameter values are not always passed from left to right

Parameters may not be evaluated from left to right. Why do you think they should? I did not find anything in the Help, which indicated a left-to-right parameter evaluation.

It is bad programming style, anyway. Your code should not depend on side effects.

engunneer
  • Moderators
  • 9162 posts
  • Last active: Sep 12 2014 10:36 PM
  • Joined: 30 Aug 2005
while I can see your problem ( I think ), I would not rely on the math to be done in any certain order. I can't find any examples in the documentation that show that you can do math inside the parameter of a function call, even though I see it done a lot in the forum. Since it is not documented that it will be done a certain way, I wouldn't expect it to always perform the same.

The closest thing I can find is that the expression is evaluated as such, with += and similar being done first, and the function call being last

;index = 1, A_LoopField = 10
SurpriseMe(index, index += A_LoopField)
;becomes
;index = 1, A_LoopField = 10
SurpriseMe(index, index)
;becomes
;index = 11, A_LoopField = 10
SurpriseMe(11, 11)

So AHK is following the precedence from the Expressions page, and evaluating the operators completely before doing the final substitution. This assumes that the function call is last after Mod(), Round(), etc, which is not stated directly.


I have to say odd, but not a bug.

In your second example, you are forcing it to evaluate the first argument first due to left-right precedence, where the comma is evaluated next to last, so everything left of the comma is evaluated before anything to the right is.

By that logic,
List = 10,12
Index = 1
Loop, Parse, List, `,
   SurpriseMe(index += A_LoopField, index-0)

SurpriseMe(startIndex, endIndex) {
   MsgBox, %startIndex% %endIndex%
}
should give us
;index = 1, A_LoopField = 10
SurpriseMe(index += A_LoopField, index-0)
;becomes
;index = 1, A_LoopField = 10
SurpriseMe(index += 10, index-0)
;becomes
;index = 11, A_LoopField = 10
SurpriseMe(index, index-0)
;becomes
;index = 11, A_LoopField = 10
SurpriseMe(index, index)
;becomes
;index = 11, A_LoopField = 10
SurpriseMe(11, 11)

and
List = 10,12
Index = 1
Loop, Parse, List, `,
   SurpriseMe(index += A_LoopField, index-1)

SurpriseMe(startIndex, endIndex) {
   MsgBox, %startIndex% %endIndex%
}
should give us
;index = 1, A_LoopField = 10
SurpriseMe(index += A_LoopField, index-1)
;becomes
;index = 1, A_LoopField = 10
SurpriseMe(index += 10, index-1)
;becomes
;index = 11, A_LoopField = 10
SurpriseMe(index, index-1)
;becomes
;index = 11, A_LoopField = 10
SurpriseMe(11, 10)

both of which are correct.

Xander
  • Members
  • 140 posts
  • Last active: Nov 13 2007 08:31 PM
  • Joined: 23 Sep 2007

while I can see your problem ( I think ), I would not rely on the math to be done in any certain order. I can't find any examples in the documentation that show that you can do math inside the parameter of a function call, even though I see it done a lot in the forum.

I can see your trouble finding it in the help, it says in more words than needed that parameters are evaluated as expressions:

Since a function call is an expression, any variable names in its parameter list should not be enclosed in percent signs. By contrast, literal strings should be enclosed in double quotes.

Are you saying everytime i want to pass a paramter to a function I should create a variable, assign to the variable what i want to pass, and then proceed to pass it? Seems to make more confusing code than just writing the expression as the paramater in my opinion. Especially if i need to pass a lot of paramters:

a := i
i += 1
b := i / k
c := strA . " " . strB
doSomething(a, b, c)

whereas in almost any other language i've used i can simply write:
doSomething(i, ++i/k, strA . " " . strB)

Perhaps it should go in the wish list if you want to insist that it isn't a bug. It certainly is a surprise.

And by the way, yes it is plainly obviously to see what is going on, more generally it is like so:

parameters that are determined to be expression needing evaluation are done and placed in a variable that is passed to the function, namely doSomething(i, i += something) becomes something more like

a := i += something
doSomething(i, a)

where a is some temporary variable address

and in the second example it is something like

a := i - 0
b := i += something
doSomething(a, b)

Thus you get the left to right evaluation.

engunneer
  • Moderators
  • 9162 posts
  • Last active: Sep 12 2014 10:36 PM
  • Joined: 30 Aug 2005
I agree entirely that it is a surprise, but looking into it, it makes sense the way it is working. It should at least be noted somehow in the manual. since each argument is evaluated as an expression, I would expect your doSomething to also increment i before it gets to i/k, so i/k would use the now larger i.

IANAP, but I don't think C would wait for the entire function call to complete before i is incremented. If it does, then This could maybe be a bug, but I think it will only apply to ++ and --, not +=, etc.

Xander
  • Members
  • 140 posts
  • Last active: Nov 13 2007 08:31 PM
  • Joined: 23 Sep 2007

IANAP, but I don't think C would wait for the entire function call to complete before i is incremented. If it does, then This could maybe be a bug, but I think it will only apply to ++ and --, not +=, etc.

I don't use C, but I would expect it to behave as Java does since Java is largely derived from C/C++. That is, the parameters are evaluated left to right and then passed to the function.

Note though that if i was passed ByRef, I would then expect i to be the incremented value..

Also I don't follow your differentiation between ++ and += for instance.

j := ++i
is shorthand for:
i += 1
j := i

and

j := i++
is shorthand for:
j := i
i += 1

It should make no difference if += is used or ++ is used, when it gets translated to machine code, it's all the same.

engunneer
  • Moderators
  • 9162 posts
  • Last active: Sep 12 2014 10:36 PM
  • Joined: 30 Aug 2005
++ can be prefix or postfix, which will determine when it is applied. so is += is prefix, not postfix, if anything. ++ may logically be shorthand for += 1, but I don't think it is done that way internally. I think internally, i += 1 is closest to ++i, not i++, which of course makes a huge difference in the function call.

AHK is interpreted, not compiled, so the same rules may not apply as far as machine code.

Xander
  • Members
  • 140 posts
  • Last active: Nov 13 2007 08:31 PM
  • Joined: 23 Sep 2007

if anything. ++ may logically be shorthand for += 1, but I don't think it is done that way internally. I think internally, i += 1 is closest to ++i, not i++, which of course makes a huge difference in the function call.

It is done precisely the way I showed in the post above.

AHK is interpreted, not compiled, so the same rules may not apply as far as machine code.

Interpreted languages still use machinge code... that is they translate the script or what have you at runtime into machine code. Compiled langauges simply save the machine code instructions directly to file (when compiled).

It really is a no brainer what is expected from the results of the first example with ++ postfix or prefix in place of +=

That is, I would expect

index = 1
SurpriseMe(index, ++index)

to show "2 2"

index = 1
SurpriseMe(index, index++)

to show "2 1"

index = 1
SurpriseMe(index-0, index++)

to show "1 1"

and testing confirms this.

However, it would be more intuitive to have it show "1 2", "1 1" and "1 1" respectively.

engunneer
  • Moderators
  • 9162 posts
  • Last active: Sep 12 2014 10:36 PM
  • Joined: 30 Aug 2005
I'm not disputing that it is not intuitive. Just saying it is explainable.

I am not sure it can/should be changed, that is up to Chris.

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Anyone relying on undocumented features could experience surprises. It is no surprise ;-).
As I said above, the parameter evaluation order is not specified in the AHK documentation.
No code should depend on side effects. It is bad style, and should be discouraged.

Xander
  • Members
  • 140 posts
  • Last active: Nov 13 2007 08:31 PM
  • Joined: 23 Sep 2007

Anyone relying on undocumented features could experience surprises. It is no surprise ;-).
As I said above, the parameter evaluation order is not specified in the AHK documentation.
No code should depend on side effects. It is bad style, and should be discouraged.

HAH! It is one thing to say something like:

(i += (j-- * k ^= i))

is bad style and quite another to proclaim that:

i, ++i, j-- * k

is bad style!

Especially since it is in the documentation that comma separated list of expressions are evalutated from left to right.

Comma (multi-statement) [v1.0.46+]. Commas may be used to write multiple sub-expressions on a single line. This is most commonly used to group together multiple assignments or function calls. For example: x:=1, y+=2, ++index, func() [but see comma performance]. Such statements are executed in order from left to right. Note: A line that begins with a comma (or any other operator) is automatically appended to the line above it.


A parameter list to me is just a comma separated list of expressions but apparently they are not treated as such!

Thus, it should either be fixed or explicitly stated in the documentation that a list of parameters is not treated the same as a list of expressions.

If that latter route is taken, AHK would unfortunately be a language that violates the Principle of Least Astonishment, hence the function name SurpriseMe.

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
I think it's a bug. Take for example:

index = 1
Loop, 2
	SurpriseMe(index, index++)

In true left-to-right evaluation languages such as C#, Javascript and PHP the result is 1, 1 and 2, 2 whereas AutoHotkey gives 2, 1 and 3, 2. However if we use an explicit multi-statement expression we get the correct values:

i = 1
Loop, 2 {
	j := i, k := i++
	SurpriseMe(j, k)
}

Thus proving that function parameters aren't evaluated as multi-statements when they should be.

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007
It doesn't prove anything, just a guess.
It may be done this way to ease the preparation of the stack.
Notice the addresses of parameters.

Test(1,2,3,4)

Test(prm1, prm2, prm3, prm4){
	MsgBox, % &prm1 . "|" . &prm2 . "|" . &prm3 . "|" . &prm4
}


polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012

It doesn't prove anything, just a guess.

Did you read my last sentence? I thought I made it clearly evident but if I missed something could you please explain?

Notice the addresses of parameters.

That tells me that the evaluation order is reversed. Is this normal behaviour or flawed design? The docs should mention this.

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

It doesn't prove anything, just a guess.

Did you read my last sentence? I thought I made it clearly evident but if I missed something could you please explain?

Please calm down. This message referred to the following message of mine, neither yours nor other's.