numbers/strings in expressions

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

numbers/strings in expressions

17 Sep 2018, 21:43

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

- My general rules for numeric/alphabetical comparison are these:
if both items are literal strings e.g. "1" or "a", compare alphabetically
else if either item looks non-numeric, compare alphabetically
else compare numerically

- These are the rules for AHK v2. Identical apart from the 'String' line which I would consider removing.
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

- I might consider instead as the first line:
if either item is a literal string e.g. "1" or "a", compare alphabetically
- Or:
if the first item is a literal string e.g. "1" or "a", compare alphabetically
- (This would be so that you'd only have to make one of the two parameters a string literal in order to force a string comparison. I don't have a strong opinion on both/either/first.)
- [EDIT:] Needing both is advantageous, because a function might return a number as a string, and that would then force a string comparison (when used in an expression), when most often you'd want a numeric comparison.

- [EDIT:] By 'both items are literal strings' I mean something like this: ("1" < "a"), both are literal *at the point of comparison*.
- If you assign numeric-looking literal strings to variables, in AHK v1, and compare the variables, since the strings are not literal strings *at the point of comparison*, they will be compared numerically.
- You can force something to be considered as a string *at the point of comparison* by concatenating it with a blank/non-blank literal string, or by returning it from a function e.g. SubStr.

- Another potentially useful innovation, in addition to, but not instead of comparison operators, would be: StrCmp/NumCmp functions to force a numeric/alphabetical comparison, and/or 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
- [EDIT:] A StrCompare function was added to AHK v2.

- Here are some examples of forcing an alphabetical comparison:

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
MsgBox(String(2) < String(10)) ;0
MsgBox(Str(2) < Str(10)) ;0 ;a proposal
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
}

;==================================================
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 v2 Development”

Who is online

Users browsing this forum: formicant and 5 guests