numbers/strings in expressions

Discuss the future of the AutoHotkey language
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

numbers/strings in expressions

17 Sep 2018, 21:43

- These are the rules for comparison operators in AHK v2:
if both items are literal strings e.g. "1" or "a", compare alphabetically, [EDIT: I'd remove this]
else if both items are variables with type 'String', compare alphabetically, [I'd remove this]
else if either item looks non-numeric, compare alphabetically,
else compare numerically

- So, to avoid as many problems as possible, and to be as newbie-friendly as possible, I use the general principle that:
if both items look numeric, they should be compared numerically,
otherwise they should be compared alphabetically.
- However, there should be a way to force two items to be compared alphabetically.
- [EDIT:] A StrCompare function was added to AHK v2.

- A useful innovation, in addition to, but not instead of comparison operators, would be: StrCmp/NumCmp functions to force a numeric/alphabetical comparison.
- [EDIT:] A StrCompare function was added to AHK v2.
- You could also have StrOp/NumOp functions to use an operator dynamically e.g. StrOp(a, "<", b).
NumOp and StrOp: 'dynamic' operators - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=55926
- I've also proposed Num (similar to Integer/Float and '+0') and IsNum functions.
Wish List 2.0 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=13&t=36789

- For reference, here are some examples of forcing an alphabetical comparison (under the AHK v2.0-a099 rules):

Code: Select all

;examples of forcing an alphabetical comparison:

;numeric:
MsgBox(2 < 10) ;1
MsgBox(2 < SubStr("10", 1)) ;1
MsgBox(SubStr("2", 1) < 10) ;1
MsgBox(0+SubStr("2", 1) < SubStr("10", 1)) ;1 ;note: use of '0+'

;alphabetical:
n2 := 2, n10 := 10
MsgBox("2" < "10") ;0
MsgBox("" 2 < "" 10) ;0
MsgBox("" n2 < "" n10) ;0
MsgBox(SubStr("2", 1) < SubStr("10", 1)) ;0 ;note: users would need to be warned of this [AHK v1: 1]
MsgBox(String(2) < String(10)) ;0 [AHK v1: 1]
MsgBox(Str(2) < Str(10)) ;0 ;a proposal [AHK v1: 1]
MsgBox(StrOp(2, "<", 10)) ;0 ;a proposal
- I performed various tests on expressions like so in AHK v1 and AHK v2:
'var1 + var2'
'var1 = var2'
'var1 < var2'
- I used the results to inform my views. Thanks for reading.

Code: Select all

numbers/strings in expressions: test results
tested on AHK v1.1.30.00 and AHK v2.0-a099

links:

Variables and Expressions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Variables.htm#AddSub
Variables and Expressions - Definition & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/Variables.htm#AddSub

Variables and Expressions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Variables.htm#equal
Variables and Expressions - Definition & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/Variables.htm#equal

Variables and Expressions - Definition & Usage | AutoHotkey
https://autohotkey.com/docs/Variables.htm#compare
Variables and Expressions - Definition & Usage | AutoHotkey v2
https://lexikos.github.io/v2/docs/Variables.htm#compare

summaries:

+ (AHK v1):
(if first item is a literal string, append strings)
error if either item looks non-numeric
error if either item is a literal string
else add

+ (AHK v2):
error if either item looks non-numeric
error if first item is a literal string
else add

= and < (AHK v1):
if either item is a literal string e.g. "1" or "a", compare alphabetically
else if either item looks non-numeric, compare alphabetically
else compare numerically

= and < (AHK v2):
if both items are literal strings e.g. "1" or "a", compare alphabetically
else if both items are variables with type 'String', compare alphabetically
else if either item looks non-numeric, compare alphabetically
else compare numerically

tests:

AHK number/string test 1: +
AHK number/string test 2: =
AHK number/string test 3: <
AHK number/string test 4: < (further)

note: add a leading space to the first column to paste into Excel without losing double quotes
note: any leading zeros are lost for numbers when pasting into Excel

AHK number/string test 1: +
where n1 := 1, s1 := "1", s01 := "01", sa := "a"
script	AHK v1	AHK v2
n1 + n1	2	2
n1 + s1	2	2
n1 + s01	2	2
n1 + sa		err
n1 + 1	2	2
n1 + "1"		2
n1 + "01"		2
n1 + "a"		err
s1 + n1	2	2
s1 + s1	2	2
s1 + s01	2	2
s1 + sa		err
s1 + 1	2	2
s1 + "1"		2
s1 + "01"		2
s1 + "a"		err
s01 + n1	2	2
s01 + s1	2	2
s01 + s01	2	2
s01 + sa		err
s01 + 1	2	2
s01 + "1"		2
s01 + "01"		2
s01 + "a"		err
sa + n1		err
sa + s1		err
sa + s01		err
sa + sa		err
sa + 1		err
sa + "1"		err
sa + "01"		err
sa + "a"		err
1 + n1	2	2
1 + s1	2	2
1 + s01	2	2
1 + sa		err
1 + 1	2	2
1 + "1"		2
1 + "01"		2
1 + "a"		err
"1" + n1	11	err [Unexpected operator following literal string.__     Specifically: + n1)]
"1" + s1	11	err [Unexpected operator following literal string.__     Specifically: + s1)]
"1" + s01	101	err [Unexpected operator following literal string.__     Specifically: + s01)]
"1" + sa	1a	err [Unexpected operator following literal string.__     Specifically: + sa)]
"1" + 1	11	err [Unexpected operator following literal string.__     Specifically: + 1)]
"1" + "1"	11	err [Unexpected operator following literal string.__     Specifically: + "1")]
"1" + "01"	101	err [Unexpected operator following literal string.__     Specifically: + "01")]
"1" + "a"	1a	err [Unexpected operator following literal string.__     Specifically: + "a")]
"01" + n1	011	err [Unexpected operator following literal string.__     Specifically: + n1)]
"01" + s1	011	err [Unexpected operator following literal string.__     Specifically: + s1)]
"01" + s01	0101	err [Unexpected operator following literal string.__     Specifically: + s01)]
"01" + sa	01a	err [Unexpected operator following literal string.__     Specifically: + sa)]
"01" + 1	011	err [Unexpected operator following literal string.__     Specifically: + 1)]
"01" + "1"	011	err [Unexpected operator following literal string.__     Specifically: + "1")]
"01" + "01"	0101	err [Unexpected operator following literal string.__     Specifically: + "01")]
"01" + "a"	01a	err [Unexpected operator following literal string.__     Specifically: + "a")]
"a" + n1	a1	err [Unexpected operator following literal string.__     Specifically: + n1)]
"a" + s1	a1	err [Unexpected operator following literal string.__     Specifically: + s1)]
"a" + s01	a01	err [Unexpected operator following literal string.__     Specifically: + s01)]
"a" + sa	aa	err [Unexpected operator following literal string.__     Specifically: + sa)]
"a" + 1	a1	err [Unexpected operator following literal string.__     Specifically: + 1)]
"a" + "1"	a1	err [Unexpected operator following literal string.__     Specifically: + "1")]
"a" + "01"	a01	err [Unexpected operator following literal string.__     Specifically: + "01")]
"a" + "a"	aa	err [Unexpected operator following literal string.__     Specifically: + "a")]

AHK number/string test 2: =
where n1 := 1, s1 := "1", s01 := "01", sa := "a"
script	AHK v1	AHK v2
n1 = n1	1	1
n1 = s1	1	1
n1 = s01	1	1
n1 = sa	0	0
n1 = 1	1	1
n1 = "1"	1	1
n1 = "01"	0	1
n1 = "a"	0	0
s1 = n1	1	1
s1 = s1	1	1
s1 = s01	1	0
s1 = sa	0	0
s1 = 1	1	1
s1 = "1"	1	1
s1 = "01"	0	0
s1 = "a"	0	0
s01 = n1	1	1
s01 = s1	1	0
s01 = s01	1	1
s01 = sa	0	0
s01 = 1	1	1
s01 = "1"	0	0
s01 = "01"	1	1
s01 = "a"	0	0
sa = n1	0	0
sa = s1	0	0
sa = s01	0	0
sa = sa	1	1
sa = 1	0	0
sa = "1"	0	0
sa = "01"	0	0
sa = "a"	1	1
1 = n1	1	1
1 = s1	1	1
1 = s01	1	1
1 = sa	0	0
1 = 1	1	1
1 = "1"	1	1
1 = "01"	0	1
1 = "a"	0	0
"1" = n1	1	1
"1" = s1	1	1
"1" = s01	0	0
"1" = sa	0	0
"1" = 1	1	1
"1" = "1"	1	1
"1" = "01"	0	0
"1" = "a"	0	0
"01" = n1	0	1
"01" = s1	0	0
"01" = s01	1	1
"01" = sa	0	0
"01" = 1	0	1
"01" = "1"	0	0
"01" = "01"	1	1
"01" = "a"	0	0
"a" = n1	0	0
"a" = s1	0	0
"a" = s01	0	0
"a" = sa	1	1
"a" = 1	0	0
"a" = "1"	0	0
"a" = "01"	0	0
"a" = "a"	1	1

AHK number/string test 3: <
where n1 := 1, s1 := "1", s01 := "01", sa := "a"
script	AHK v1	AHK v2
n1 < n1	0	0
n1 < s1	0	0
n1 < s01	0	0
n1 < sa	1	1
n1 < 1	0	0
n1 < "1"	0	0
n1 < "01"	0	0
n1 < "a"	1	1
s1 < n1	0	0
s1 < s1	0	0
s1 < s01	0	0
s1 < sa	1	1
s1 < 1	0	0
s1 < "1"	0	0
s1 < "01"	0	0
s1 < "a"	1	1
s01 < n1	0	0
s01 < s1	0	1
s01 < s01	0	0
s01 < sa	1	1
s01 < 1	0	0
s01 < "1"	1	1
s01 < "01"	0	0
s01 < "a"	1	1
sa < n1	0	0
sa < s1	0	0
sa < s01	0	0
sa < sa	0	0
sa < 1	0	0
sa < "1"	0	0
sa < "01"	0	0
sa < "a"	0	0
1 < n1	0	0
1 < s1	0	0
1 < s01	0	0
1 < sa	1	1
1 < 1	0	0
1 < "1"	0	0
1 < "01"	0	0
1 < "a"	1	1
"1" < n1	0	0
"1" < s1	0	0
"1" < s01	0	0
"1" < sa	1	1
"1" < 1	0	0
"1" < "1"	0	0
"1" < "01"	0	0
"1" < "a"	1	1
"01" < n1	1	0
"01" < s1	1	1
"01" < s01	0	0
"01" < sa	1	1
"01" < 1	1	0
"01" < "1"	1	1
"01" < "01"	0	0
"01" < "a"	1	1
"a" < n1	0	0
"a" < s1	0	0
"a" < s01	0	0
"a" < sa	0	0
"a" < 1	0	0
"a" < "1"	0	0
"a" < "01"	0	0
"a" < "a"	0	0

AHK number/string test 4: < (further)
where n10 := 10, n2 := 2, s10 := "10", s2 := "2"
script	AHK v1	AHK v2
n10 < n10	0	0
n10 < n2	0	0
n10 < s10	0	0
n10 < s2	0	0
n10 < 10	0	0
n10 < 2	0	0
n10 < "10"	0	0
n10 < "2"	1	0
n2 < n10	1	1
n2 < n2	0	0
n2 < s10	1	1
n2 < s2	0	0
n2 < 10	1	1
n2 < 2	0	0
n2 < "10"	0	1
n2 < "2"	0	0
s10 < n10	0	0
s10 < n2	0	0
s10 < s10	0	0
s10 < s2	0	1
s10 < 10	0	0
s10 < 2	0	0
s10 < "10"	0	0
s10 < "2"	1	1
s2 < n10	1	1
s2 < n2	0	0
s2 < s10	1	0
s2 < s2	0	0
s2 < 10	1	1
s2 < 2	0	0
s2 < "10"	0	0
s2 < "2"	0	0
10 < n10	0	0
10 < n2	0	0
10 < s10	0	0
10 < s2	0	0
10 < 10	0	0
10 < 2	0	0
10 < "10"	0	0
10 < "2"	1	0
2 < n10	1	1
2 < n2	0	0
2 < s10	1	1
2 < s2	0	0
2 < 10	1	1
2 < 2	0	0
2 < "10"	0	1
2 < "2"	0	0
"10" < n10	0	0
"10" < n2	1	0
"10" < s10	0	0
"10" < s2	1	1
"10" < 10	0	0
"10" < 2	1	0
"10" < "10"	0	0
"10" < "2"	1	1
"2" < n10	0	1
"2" < n2	0	0
"2" < s10	0	0
"2" < s2	0	0
"2" < 10	0	1
"2" < 2	0	0
"2" < "10"	0	0
"2" < "2"	0	0

Code: Select all

;==================================================

;numbers/strings in expressions
;test 'a + b', 'a = b', 'a < b' etc
;note: puts text onto the clipboard at the end
;note: can be a little slow

;==================================================

#SingleInstance force
ListLines, Off
#KeyHistory 0
Menu, Tray, Click, 1
#NoEnv
AutoTrim, Off
#UseHook

SplitPath, A_ScriptName,,,, vScriptNameNoExt
Menu, Tray, Tip, % vScriptNameNoExt

;==================================================

vPathTemp := A_ScriptDir "\z temp str num " A_Now ".ahk"
vPathAhk1 := A_AhkPath
vPathAhk2 := "C:\Program Files\AutoHotkey v2\AutoHotkeyU32.exe"
if FileExist(vPathTemp)
	throw "error: file already exists:`r`n" vPathTemp
if !FileExist(vPathAhk1)
	throw "error: file not found:`r`n" vPathAhk1
if !FileExist(vPathAhk2)
	throw "error: file not found:`r`n" vPathAhk2

;==============================
if 1
{
	vScriptA = ;continuation section
	(LTrim Join,`s
		n1 := 1
		s1 := "1"
		s01 := "01"
		sa := "a"
	)
	vListVar = n1,s1,s01,sa,1,"1","01","a"
}
;==============================
if 0
{
	vScriptA = ;continuation section
	(LTrim Join,`s
		n10 := 10
		n2 := 2
		s10 := "10"
		s2 := "2"
	)
	vListVar = n10,n2,s10,s2,10,2,"10","2"
}
;==============================
vOp := "+"
vOp := "="
vOp := "<"
;==============================

vDQ := Chr(34)
vScriptC1 = FileAppend, `% var, `% "*"
vScriptC2 = FileAppend(var, "*")
oVar := StrSplit(vListVar, ",")
VarSetCapacity(vOutput, 1000*2)
vOutput := "where " vScriptA "`r`n"
vOutput .= "script`t" "AHK v1`t" "AHK v2`r`n"
Loop % oVar.Length()
{
	vItem1 := oVar[A_Index]
	Loop % oVar.Length()
	{
		vItem2 := oVar[A_Index]
		vScriptB := "try var := (" vItem1 " " vOp " " vItem2 ")"
		vScriptB .= "`r`n" "catch"
		vScriptB .= "`r`n" "`tvar := " vDQ "err" vDQ
		Loop 2
		{
			vScript := vScriptA "`r`n" vScriptB "`r`n" vScriptC%A_Index%
			;MsgBox, % vScript
			FileAppend, % vScript, % "*" vPathTemp, UTF-8

			;vTarget = "%vPathAhk%" /ErrorStdOut "%vPathTemp%"
			vTarget := vDQ vPathAhk%A_Index% vDQ " /ErrorStdOut " vDQ vPathTemp vDQ
			vPathAhk := vPathAhk%A_Index%
			vRet := JEE_RunGetStdErr(vTarget)
			vRet := RTrim(vRet, "`r`n")
			vRet := RegExReplace(vRet, "[`r`n]", "_")
			vRet := RegExReplace(vRet, "^.*==> ")
			;if !(vRet = "")
			;	MsgBox, % vScript
			if !(vRet = "")
				;vRet%A_Index% := "err(pre)"
				vRet%A_Index% := "err [" vRet "]"
			else
				vRet%A_Index% := JEE_RunGetStdOut(vTarget)
			FileOpen(vPathTemp, "w").Close() ;empty file
		}
		vOutput .= vItem1 " " vOp " " vItem2 "`t" vRet1 "`t" vRet2 "`r`n"
	}
}

FileDelete, % vPathTemp
Clipboard := vOutput
MsgBox, % "done"
return

;==================================================

JEE_RunGetStdErr(vTarget, vSize:="")
{
	DetectHiddenWindows, On
	vComSpec := A_ComSpec ? A_ComSpec : ComSpec
	Run, % vComSpec,, Hide, vPID
	WinWait, % "ahk_pid " vPID
	DllCall("kernel32\AttachConsole", "UInt",vPID)
	oShell := ComObjCreate("WScript.Shell")
	oExec := oShell.Exec(vTarget)
	vStdErr := ""
	if !(vSize = "")
		VarSetCapacity(vStdErr, vSize)
	while !oExec.StdErr.AtEndOfStream
		vStdErr := oExec.StdErr.ReadAll()
	DllCall("kernel32\FreeConsole")
	Process, Close, % vPID
	return vStdErr
}

;==================================================

JEE_RunGetStdOut(vTarget, vSize:="")
{
	DetectHiddenWindows, On
	vComSpec := A_ComSpec ? A_ComSpec : ComSpec
	Run, % vComSpec,, Hide, vPID
	WinWait, % "ahk_pid " vPID
	DllCall("kernel32\AttachConsole", "UInt",vPID)
	oShell := ComObjCreate("WScript.Shell")
	oExec := oShell.Exec(vTarget)
	vStdOut := ""
	if !(vSize = "")
		VarSetCapacity(vStdOut, vSize)
	while !oExec.StdOut.AtEndOfStream
		vStdOut := oExec.StdOut.ReadAll()
	DllCall("kernel32\FreeConsole")
	Process, Close, % vPID
	return vStdOut
}

;==================================================
Last edited by jeeswg on 10 Oct 2019, 17:41, edited 1 time in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: numbers/strings in expressions

10 Oct 2019, 17:33

- I've revised my conclusion above to the simple criteria:
if both items look numeric, they should be compared numerically,
otherwise they should be compared alphabetically.

- StrCompare gives us a way to force two numeric-looking items to be compared alphabetically.

- I might also suggest a StrEquals/StrMatch function, similar to StrCompare but with more intuitive return values.
- E.g. StrEquals(var1, var2, "<=").
- (It could have a very versatile Options parameter cf. the versatility of RegExMatch/RegExReplace.)

- For more info, see: IN/CONTAINS/STARTS/ENDS(/STRMATCH):
Wish List 2.0 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=13&t=36789
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

Return to “AutoHotkey Development”

Who is online

Users browsing this forum: OpalMonkey and 110 guests