Page 1 of 1

### numbers/strings in expressions

Posted: 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

Variables and Expressions - Definition & Usage | AutoHotkey
Variables and Expressions - Definition & Usage | AutoHotkey v2

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

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

= 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
#NoEnv
AutoTrim, Off
#UseHook

SplitPath, A_ScriptName,,,, 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)
if !FileExist(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
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