原文:知乎 虚荣
- 1. 绵长的前戏
- ... 1.1 示例
- ... 1.2. AHK的对象是字典
- ... 1.3. 字典里面都是些什么
- ... 1.4. 数组跟字典是什么关系
- ... 1.5. AHK的类与对象可以看作字典
- ... 1.6. 键指向什么内容呐
- ... 1.7. 复杂的嵌套对象好比是道路
- ... 1.8. 对象附带的方法
- ... 1.9. 写在前头: 一个非常非常重要的结论
- 2. 开始正戏: 对象的声明
- ... 2.1. 声明空对象
- ... 2.2. 定义非空对象与键的数据类型
- ...... 2.2.1. 非空字典(关联数组)定义
- ...... 2.2.2. 如何用表达式定义键?
- ...... 2.2.3. 定义非空数组
- ...... 2.2.4. 重定义对象
- ...... 2.2.5. 对象的键在该对象中具有唯一性
- ...... 2.2.6. 定义时写入了重复的键
- ...... 2.2.7. 特别的 (对象类型的键)
- ...... 2.2.8. 浮点数键是个什么键
- ...... 2.2.9. 字符串键不分大小写
- 3. 增添内容至对象
- ... 3.1. 给对象中 不存在的键 赋值
- ... 3.2. 用于 数组 的 插入键值对的方法
- ...... 3.2.1. .Push() 方法 添加内容到数组的末尾
- ...... 3.2.2. .InsertAt() 方法 在数组某个位置插入值
- 4. 对象的查询办法
- ... 4.1. 取某个键的值
- ... 4.2. 判断一个变量是否为对象
- ... 4.3. 对象中是否有某个键
- ... 4.4. 键值对数量
- ... 4.5. 数组关于数字键的3个方法
- 5. for 循环与对象(重要)
- ... 5.1. 基本格式
- ... 5.2. for 循环作为循环语句
- ... 5.3. for 循环之于数组的重要性
- ... 5.4. 临时存放键值的两个变量
- ... 5.5. (非对象)或(空对象)引用 for 循环语句
- ... 5.6. for 循环 对 字符串键 遍历 顺序逻辑
- 6. 未完待续...
1. 绵长的前戏
1.1 示例
今天,我们要用上很有内容的 对象,以下示例可以粗略看过。
集装箱 := {}
;~ ------ 首先, 表示它是很有内容的(用{}大括号声明一个对象)
集装箱["里面"] := "内容"
;~ ------ 然后, 我们在它 的 "里面" 放上 "内容"
Msgbox, % 集装箱["里面"]
;~ ------ 接着, 我们通过 "里面" 把这个东西拿出来晒一晒
集装箱["别的标签"] := "新来的神秘物体"
;~ ------ 继续, 装点别的东西
s := "别的标签"
Msgbox, % 集装箱[s]
;~ ------ 通过一个变量把新的神秘物体弄出来
a := "别的"
b := "标签"
Msgbox, % 集装箱[a . b]
;~ ------ 通过2个变量拼接把新的神秘物体弄出来
c := "别"
Msgbox, % 集装箱[c . "的标签"]
;~ ------ 再来, 通过1个变量和1个字符串拼接做同样的事
Msgbox, % 集装箱.count() . " 个内容是至今这个集装箱里总共有的"
;~ ------ 我们到底放了多少东西呢, .count()是集装箱的方法之一
for 标签, 内容 in 集装箱
msgbox, % 标签 "`n" 内容
;~ ------ 用for循环语句分别掏出来瞅一瞅
集装箱 := {"标1": "物1", "标2" : "物2"}
;~ 我们重新定义它
s := ""
for k, v in 集装箱
s .= k . "=" . v . "`n"
msgbox, % s
;~ ------ 可以发现最初定义的东西消失了,
;~ ------ 这是用for循环拼接一个字符串s, 然后一次性显示在信息窗
1.2. AHK的对象是字典
这样的集装箱,在AHK文档中被称为 Object / 对象, 或细分可称之(关联数组), 在python中有个近似的数据类型, 字典,以下就以字典称呼它(关联数组)好了。
1.3. 字典里面都是些什么
字典的组成单位是 键值对,一个字典中有一些 键(索引),分别指向各自的值(内容),这样的概念被称作 键值对。
对象 := {"键名" : "值"}
Msgbox, % 对象["键名"]
1.4. 数组跟字典是什么关系
以字典开篇示例,而不以看似更简单的数组起头,个中原因:
在AHK中,数字像是可以运算的特殊的字符串。
而 数组(Array) 则像是 区分了键形式 (限制键内容为数字) ,拓展了 键 作为数字 可计算 功能 及 有序 性质的 字典,这里下个结论,AHK的 数组 就是 字典 ( 数组 是 关联数组 的一种特殊表现)。
对象 := {2 : 34567}
Msgbox, % 对象[1+1]
1.5. AHK的类与对象可以看作字典
如果你在搜索引擎搜过 类 或者 对象,一般会注意两个高频出现的词汇,属性和方法,说白了,它们是封装在一个对象里面的 变量 和 函数
贴近AHK一些,对象的 属性/方法 的 索引 就是对象中的键
1.6. 键指向什么内容呐
键 是一个索引,键所指向的事物是所有AHK可定义的数据类型
对象 := {}
;~ 1)可以是一个简单的数值,即它键的值是一个简单的参数
对象["某参1"] := 333
对象["某参2"] := "一行字"
MsgBox, % 对象["某参1"] ;~ 弹窗 333
MsgBox, % 对象["某参2"] ;~ 弹窗 一行字
;~ 2)是一个方法(功能函数),可以使用它的功能达成一个目的
消息() {
static 引用次数
引用次数 += 1
Msgbox, 弹出一个消息`n引用次数为%引用次数%
}
对象["弹窗"] := func("消息")
对象["弹窗"]()
;~ 3)是另一个对象(对象的键索引至有其他关联键值对的对象)
即将被扔进另一个对象 := {"键": "别的键的值"}
对象["小天地"] := 即将被扔进另一个对象
Msgbox, % 对象["小天地"]["键"]
1.7. 复杂的嵌套对象好比是道路
对象可以是一个复杂的集体,你想象是一个坐标原点。
从这个对象的首都(对象本身)出发,从主干道分成一条条支路
键 是每条道路的唯一的指示牌,而对象的键的值可以指向另一个对象,即这条支路又分出了其他道路,也同样以路牌指示。
做一个不正经的示例:
换个例子可以是我们日常接触的文件路径
假如有个天大的对象C盘
C := {"国学" : {"儒学" : {"荀子": {"温碧霞":"香魂.rmvb", "舒淇":"玉蒲团.avi", "叶玉卿":"卿本佳人.flv","快播":func("播放"), "高清写真" : {....})}}}}
我们可以看出在
C:\国学\儒学\荀子\
这个路径下有很多姿势,包括3大著名女星的视频文件,包括一个可执行的快播程序,还有一个名为高清写真的文件夹
假如我们要运行纯洁的快播呢
就酱:
好地方 := C["国学"]["儒学"]["荀子"]
好地方["快播"](好地方["舒淇"])
由于藏得比较深,所以这个路径有点长,当然,正经的我们知道这只是一个字典索引的示例。
1.8. 对象附带的方法
在创建字典时,同时附带它作为字典的方法
而索引向这些方法的键,是被隐藏的,无法被for 循环取得。
;~ 譬如 .count() 方法,获取字典中键值对数量
字典 := {"键1":"", "键2":""}
;~ 正常我们使用方法
Msgbox, % 字典.count()
;~ 用键名取方法也是被允许的
Msgbox, % 字典["count"]()
;~ 取键的值也可以用. 英文句号
Msgbox, % 字典.键1
;~ 但是这样内置的方法可以被重写(覆盖)
字典["count"] := 233
;~ 如此,这个字典永远失去了它的方法
Msgbox, % 字典["count"] . ": 这是count的新内容"
Msgbox, % 字典["count"]() . ": 到底经历了什么, 左边空掉了"
1.9. 写在前头: 一个非常非常重要的结论
AHK 关于 简单对象, 所有所有的内容, 都可以归结为对 (键) 与 (值) 的操作
而对 ( 键 ) 的性质了解 直接 影响 对象操作 影响 AHK 编码性能
2. 开始正戏: 对象的声明
它只有是一个对象,才能执行作为对象的所有操作
2.1. 声明空对象
空对象是对象的最简实现
声明空对象,以下4种方式是等效的
对象 := object()
对象 := {}
对象 := Array()
对象 := []
2.2. 定义非空对象
在定义对象时候顺带给对象 赋予 自定义数量的 键值对 单位
2.2.1. 非空字典(关联数组)定义
/* 感谢热心网友 bootloader 指出 文章本段含有2处符号错误, 现已改正 */
以下3个定义方式是等效的
;~ 由object() 函数创建, 传入参数为一般为2的倍数, 单数位为键, 双数位为值
o1 := object("键1","1内容", "键2","2内容")
;~ 由{ } 中 的 (键 : 值) 以 (,) 分隔 方式
o2 := {"键1" : "1内容", "键2": "2内容"}
;~ 同上等效
o3 := {键1 : "1内容", 键2: "2内容"} ;键名有无双引号都视作字符串
o2 和 o3 的区别
双引号中定义的键键名中可以包含" "空格与"."句点符号 等特殊符号
包含空格与句点符号时,则不适用 对象.键名 形式的取值取值
o := {"左.右": 123} Msgbox, % ".点取值试验结果: " o.左.右 ;~ 空弹窗 Msgbox, % "[ ]取值试验结果: " o["左.右"] ;~ 123
2.2.2. 非空对象定义时怎么用变量(或表达式的结果)作为键?
加上括号即可。
变量 := "键"
a := {变量 : 11}
Msgbox, % a[变量] ;~ 空弹窗
Msgbox, % a["键"] ;~ 空弹窗
Msgbox, % a["变量"] ;~ 弹窗11
b := {(变量) : 11}
Msgbox, % b["键"] ;~ 弹窗11
c := {( "ab" . "cd") : 123 }
Msgbox, % c["abcd"]
2.2.3. 定义非空数组
以下3种非空数组定义是等效的,
;~ a1和a2两种定义方法,解释器默默地将内容本体从左至右写上出现次序的键
;~ 是以1为首序号,步进为1的递增
a1 := Array("1内容","2内容","3内容")
a2 := ["1内容", "2内容", "3内容"]
;~ 若你心大(闲得蛋疼)定义这样的数组可以如下办法, 也是等效的
a3 := {1 : "1内容", 2 : "2内容", 3 : "3内容"}
2.2.4. 重定义对象
并非增加内容至原来对象,将删除原来的对象包括名下所有键值对
o := {"标1":123}
msgbox, % o["标1"] ;~ 弹窗123
o := {"标2":556}
msgbox, % o["标1"] ;~ 弹窗空白
2.2.5. 对象的键在该对象中具有唯一性
任意对象中不会存在两个相同的键
相同的键多次赋值即修改同一个键的内容
2.2.6. 定义时写入了重复的键
只有最后一个(最右侧)键值对生效
a := {"xxx" : 123, "xxx": 456, "xxx": 789} Msgbox, % a["xxx"] ;~ 弹窗789
2.2.7. 特别的 (对象类型的键)
a := {}
b := {}
c := {}
c[a] := 123
msgbox, % c[a] ;~ 取得123
msgbox, % c[b] ;~ 空弹窗
此处可见,包括对象类型也可以作为键索引
这里我要卖个萌,应该有特殊用途。。但我暂时想不出来。。
2.2.8. 浮点数键是个什么键
经过吃撑了的简单考察
直接说结果吧,浮点数键 不算 数字键,它只能算是 字符串键,被排除在数组规则之外。
a := {}
a[1.1] := 11
a[5] := 22
a[9.9] := 33
Msgbox, % a.maxindex()
Msgbox, % a.minindex()
;~ 两个弹窗皆返回5
Msgbox, % a[9.9] ;~ 取值正常弹窗33
2.2.9. 字符串键中的字母不分大小写
感谢 梦醒(feiyue) 大佬 补充... 不过这里我并未做过测试, 原谅我偷懒...
3. 增添内容至对象
3.1. 给对象中 不存在的键 赋值
即视作添加键值对
对象 := {"键1" : 123, "键2" :123}
;~如此行为添加内容(键值对)
对象["键3"] := 556
for 键, 值 in 对象
msgbox, % 键 "=" 值
/* 依次弹窗
键1=123
键2=123
键3=556
*/
a:= [123, 123]
a[3] := 556
for 键, 值 in a
msgbox, % 键 "=" 值
3.2. 用于 数组 的 插入键值对的方法
上头说道数组是特殊的对象
从插入 键值对 的方法也可见端疑
3.2.1. .Push() 方法 添加内容到数组的末尾
数组.Push(值)
该方法比较适用于数组
存在纯数字键的对象中,Push()方法 增加一个键在末尾。
A := [123, 123]
A.push(556)
for 键, 值 in A
msgbox, % 键 "=" 值
/* 依次弹出
1=123
2=123
3=556
*/
.Push()实则是给 一个不存在的 数值为(最大数字键+1) 的键 赋值
A := {-6: 123, -5: 456}
A.push(789)
for 键, 值 in A
msgbox, % 键 "=" 值
/* 依次弹窗:
-6=123
-5=456
-4=789
*/
;~ 等效方法:
A := {7: 123, 8: 123}
最大键 := A.maxindex()
msgbox, % 最大键 ;~弹窗8
A[最大键 + 1] := 556
for 键, 值 in A
msgbox, % 键 "=" 值
.push()方法加入多个值
对象.push(值1, 值2, ..., 值n)
从第一个 值1 赋值给 .Maxindex() + 1键开始
次位 值2 赋值给 Maxindex() + 2,依次类推,步进始终为1的赋值方法
A := [ ]
A.Push("abc")
A.Push("def")
A.Push("ghi")
for i, v in A
MsgBox, % i "=" v
/* 依次弹窗
1=abc
2=def
3=ghi
*/
但是.push()方法 对 不存在数字键的 字典 也生效
这种情况:
a := {"我不是数字键的哦" : 123}
a.push("随便放个东西")
for key in a
Msgbox,% key
依次弹窗:
> 1
> 我不是数字键哦
3.2.2. .InsertAt() 方法 在数组某个位置插入值
数组.InsertAt(索引, 插入的值)
a := ["x1", "x2", "x3"] /* 譬如有个数组a, 键值分别为 1=x1 2=x2 3=x3 */ a.InsertAt(2, "x9") /* 此时数组内容为 1=x1 2=x9 3=x2 4=x3 */
可以看出,插入位置之前的键不变,从插入位置启,其后的所有大于插入位置键的 键值对 的 键,都被推后1个位(原有的键+1)
也许这样不严谨,所以虚荣又做了另一个操蛋的试验
同.push()方法一样,.InsertAt() 方法也是可变参数,它可以一次插入多个值
;~数字键分别为1 6 10
A := {1: 111, 6: 222, 10: 333}
;~ 从第1位开始插入3个字符
A.InsertAt(1, "a", "b", "c")
for i, v in A
Msgbox, % i "=" v
/* 分别弹窗:
1=a
2=b
3=c
4=111
9=222
13=333
*/
可以看出,从插入位置启,之后原有的 键都会累加,
键 = 键 + 插入元素个数
也能看出,这种方法会改变数组原有结构。
4. 对象的查询办法
4.1. 取某个键的值
对象[ 表达式 ]
假设我们有一个对象
需要取出对应键索引的值时,要在对象名称右侧加上[ ] 小括号
对象 := {"abc": 123}
msgbox, % 对象["abc"] ;~ 弹窗123
这里要注意: 命令式中的参数无法用传统式方式 对 对象 进行 取值!
如以下两种方式都是错误的
Msgbox, %对象%["abc"]
Msgbox, %对象["abc"]%
( 对象["abc"] ) 这样的形式在表达式中视为一个整体,可以作一个变量食用,无论 赋值 还是 取值
[ ]小括号中一般是一个字符串,也可以是计算结果为 字符串 或 数字的表达式。
假设我们有一个混乱的对象
* 此处再次致谢 热心的 bootloader 同志..键名为"22"时 [11 * 2]的取值办法无效..改为无双引号的 22 可解
obj := {22 : 100
, "灵能100%": 200
, "funcRet" : 300}
;~ 取键 22 的值
msgbox, % obj[11 * 2]
;~ 取键 "灵能100%"的值
msgbox, % obj["灵能" . (20* 5) . "%"]
;~ 取键 "funcRet"的值
函数() {
return "funcRet"
}
msgbox, % obj[函数()]
4.2. 判断一个变量是否为对象
用IsObject()函数
a := 123
b := {}
c := [123]
msgbox, % IsObject(a) ;~ 返回 0
msgbox, % IsObject(b) ;~ 返回 1
msgbox, % IsObject(c) ;~ 返回 1
4.3. 对象中是否有某个键
用 .haskey(键) 方法
;~ 数组A的3个键分别为,1, 2, 3
A := ["内容", "内容", "内容"]
msgbox, % A.haskey(3) ;~ 返回1 (true)
msgbox, % A.haskey("内容") ;~ 返回0 (false)
;~ a的2个键分别为 "abc" "def"
A := {"abc": 111, "def": 222}
msgbox, % A.haskey("def") ;~ 返回1(true)
msgbox, % A.haskey(111) ;~ 返回0 (false)
4.4. 键值对数量
用 .count() 方
该方法返回该字典的键值对数量,此命令较通用
就是有多少组数值, 不做示例
4.5. 数组关于 数字键 的3个方法
.MaxIndex()/.MinIndex() 方法
该方法返回字典中 最大/最小的 整数键
在很长一段时间里,Maxindex()方法被笔者用作查询数组长度(键值对数量)的方法,但是这种方法并不严谨
在查询数组内容数量时,建议用.count()方法
这两种方法一般用于纯数组
.MaxIndex()方法在数组首位以1开始计算,且正常1位步进时,可代替.count() 获取键值对数量的方法
数组的键可以为负数如:
数组 := {-1:"",-3:"", -5:""}
msgbox, % "最大键为" . 数组.maxindex() ;~ 弹窗-1
msgbox, % "最小键为" . 数组.minindex() ;~弹窗-5
.Length() 方法
文档中描述,数组.Length() 方法与.Maxindex()类似,笔者也做过调查,返回的也是最大整数键
区别在不存在数字键时 .Length() 方法 返回0
而.Maxindex() 方法 返回空值
5. for 循环与对象(重要)
5.1. 基本格式
for 键 in 对象
{
;~ 代码块
}
for 键, 值 in 对象
{
;~ 代码块
}
可以看出此类循环格式:
由关键词 for 开头
键, 值 分别为两个变量名称,保存遍历对象时当前循环的 键 和 值,其中(值)可省
in 后接 对象, 或者是 计算结果 为 对象 的 表达式。
for i in 数组
for i, var in 数组
for key in 字典
for key, var in 字典
推荐这两种写法,从变量名做文章在某种程度上从感官上区别数组和字典
;~ 数组:
for i, v in ["abc", "def", "ghi"]
{
Msgbox,
(
当前循环次数: %A_Index%
当前取得下标: %i%
当前取得值: %v%
)
}
;~ 字典:
for key, v in {"k1": 333, "k2":666, "k3":999}
{
Msgbox,
(
当前循环次数: %A_Index%
当前取得键名: %key%
当前取得值: %v%
)
}
循环 for语句 其他惯用形式
;~ for 遍历 数组 A
A := [333, 666, 999]
for i, v in A
msgbox, % i "=" v
;~ for 遍历 函数的返回值
函数() {
return {"a":33, "b":66, "c":99}
}
for k, v in 函数()
Msgbox, % k "=" v
;~ for 遍历 二维嵌套对象
a:= {}
a["abc"] := [11, 22, 33]
a["def"] := [44, 55, 66]
a["ghi"] := [77, 88, 99]
/* 等效定义方法
a := {"abc": [11, 22, 33]
, "def": [44, 55, 66]
, "ghi": [77, 88, 99]}
*/
计次 := 0
for key, List in a {
for i, v in List {
计次 += 1
Msgbox,
(
当前取得对象a中列表: %key%
取得该列表下标: %i%
下标对应值: %v%
总计次: %计次%
)
}
}
5.2 for 循环作为循环语句
for ( ) in ( ) 语句是一个有限的循环,循环次数与对象内键值对数量相等。
即以下两个循环语句循环次数是一样的
loop, % 对象.count()
{
}
for i in 对象
{
}
同样的,拥有循环语句的特性:
break: 命令, 在任意位置结束循环
continue: 命令, 在任意位置跳过本次循环直接进行下个循环
A_Index: 内置变量, AHK特有的记录循环次数的变量, 正常情况下与正常使用的数组下标一致
5.3 for 循环之于数组的重要性
数组的有序性的特质在for循环中呈现,for 循环取出数组内容的是依键值从小到大的顺序。
键的数值越小,越先在for循环中出现,其最大的键值会出现在for循环中最后一个循环段中。
这是数组有序性质的最重要体现
for i in {11:"", 9:"", 5:"", 1:""}
msgbox, % i
5.4 临时存放键值的两个变量
除非这两个变量可能被赋予了对象中的对象
否则对两个变量的赋值更改 不会影响 到 对象本身 的 内容
且在下一次循环开头被刷新
且在循环结束 或 循环中断以后,两个变量依旧存放 最后一次循环 所 被赋予的值
5.5. (非对象)或(空对象)引用 for 循环语句
这个请放心,不会执行任何动作,一次循环都不会有
包括空对象也不会执行
5.6. for 循环 对 字符串键 遍历 顺序逻辑
笔者是个没文化土包子,偏偏喜欢计算机相关的东西,喜欢看那些高大上的算法的名词(仅限于名词),听过哈希表什么的。
常年跟AHK打交道,也觉着有迹象表明,AHK的字符串键也是有排序规律的。
本着探索精神。。还是做个不知道对不对的试验。
;~ 取得"启"字utf-16编码
code := asc("启")
MsgBox, % "启字编码为: " code
;~ 将"启"开始 编码步进为1 的所有编码对应字符依序拼接 200 个, 赋值给s1
s1 := ""
Loop, 200
s1 .= chr(code + A_Index - 1)
MsgBox, % "初次执行结果:`n" . s1
;~ 声明一个空对象
obj := {}
;~ 解析s1 每个字符作为对象 obj 的键
Loop, parse, s1
{
obj[A_LoopField] := ""
}
;~ 用 for 循环依次取出obj的键, 拼接赋值 给s2
s2 := ""
for key in obj
{
s2 .= key
}
MsgBox, % "第二次执行结果:`n" . s2
if (s1 = s2)
{
MsgBox, 这两个字符串是等值的
}
return
评论(0)