# 函数

## 目录

- 介绍以及简单的例子
- 参数
- 可选参数
- 局部变量
- 优化布尔求值
- 在函数中使用子程序
- Return, Exit 和一般说明
- 用 #Include 在多个脚本中共享函数
- 函数库：标准库和用户库
- 内置函数

## 介绍以及简单的例子

函数和子程序(Gosub)相似，只不过它能从它的调用者那里接受参数(输入)。此外，函数能随意地向它的调用者返回一个值。参考下面这个简单的函数，它接受了两个数字并返回它们的和：

    Add(x, y)
    {
     return x + y ; "Return" 需要一个表达式。
    }

上面的被称为函数定义，因为它创建了一个名为 "Add"(不区分大小写)的函数，并且确定无论谁要调用它都必须确切地提供两个参数(x 和 y)。要调用函数并将它的结果赋值给变量，用 :=运算符。例如:

    Var := Add(2, 3) ;数字 5 将存储在变量 var 中。

而且，可以调用函数而不存储它的返回值：

    Add(2, 3)

但是如果这样，任何通过函数返回的值都将被抛弃；因此除非函数除了它的返回值外还会产生其他一些效果，不然此调用将无济于事。

一旦函数在表达式中被调用，任何在它参数列表中的变量名都不能被附上百分号。相反，原义字符串却要加上双引号。例如：

    if InStr(MyVar, "fox")
       MsgBox MyVar 变量包含单词 fox。

在 v1.0.47.06 及之后版本里，一个函数(甚至是内置函数)可以通过百分号被动态地调用。例如，%Var%(x, "fox") 将调用一个名称存储在变量 Var 里的函数。相似地，Func%A_Index%() 将调用名称存储在指定数组元素中的函数。所调用的函数必须在脚本中明确地定义，也可通过 #Include 或者非动态调用一个函数库。如果被调用的函数不存在，或者给函数参数传递的值或类型不对，那么包含此调用的表达式将产生一个空字符串。

最后，函数可以被任何命令的参数调用 (除了像 StringLen 命令中的那些 OutputVar 和 InputVar 参数)。不过，那些不支持表达式的参数必须使用"%"作为前缀，例如下面这个例子：

    MsgBox % "答案是：" . Add(3, 2)

"% "前缀也允许用在本来就支持表达式的参数里，但它将被简单地忽略。

## 参数

当一个函数被定义的时候，它的参数都列在它的名称后面的括号中(它的名称和左括号之间不能有空格)。如果一个函数不接收任何参数，就让括号空着；例如: GetCurrentTimestamp()。

ByRef 参数：从函数的观点来看，参数本质上和局部变量一样，除非它们像在下面这个例子中那样被定义为 ByRef：

    Swap(ByRef Left, ByRef Right)
    {
     temp := Left
     Left := Right
     Right := temp
    }

在上面的例子中，ByRef 的使用导致各个参数变成了从调用者传递进来的变量的一个别名，换句话说，参数和调用者的变量在内存中都引用了相同的内容。这就允许了 Swap 函数通过移动 Left 的内容给 Right 来改变调用者的变量，反过来也一样。

相比之下，如果在上面的例子中没有使用 ByRef 的话，Left 和 Right 将是调用者的变量的复制，因此 Swap 函数也将没有效果。

由于 return 只能送回一个值给函数的调用者，所以 ByRef 可以用来送回更多的结果。这是由函数向调用者传递进来的变量(通常为空)储存一个值来实现的。

当向函数传递大量字符串的时候，ByRef 可以提高性能并且通过回避复制字符串的需求来节约内存。同样，使用 ByRef 送回一个长字符串给调用者往往比例如 Return 巨型字符串执行得更好。

已知限制:

- 不能向函数的 ByRef 参数传递剪贴板，内置变量或者环境变量，甚至当 #NoEnv 在脚本里不存在的时候。传递一个内置变量给一个 ByRef 参数将导致显示一个错误对话框。
- 尽管一个函数能递归地调用自身，但如果它传递它自己的一个局部变量或者非 ByRef 参数给它自身的 ByRef，新一层的 ByRef 参数将引用它自身那个局部变量的名称而不是之前的层的。不过，这个矛盾在一个函数传递一个全局变量，静态变量或者 ByRef 参数给它自身的时候不会发生。
- 如果一个参数在函数调用里被解析为一个变量（比如 Var 或 ++Var 或 Var*=2），它左边或右边的其他参数能在它被传递给函数前改变那个变量。比如，当 Var 最初为 0 时，即使当函数的首个参数不是 ByRef 时，func(Var, Var++) 仍会意外地传递 1 和 0。由于这种行为是违反常规的，所以可能在将来放出的版本中改变。

## 可选参数

在定义一个函数时，它的一个或多个参数可以被标记为可选。这可以通过给它们添加一个等号和一个默认值来实现。下面的函数的参数 Z 已标记为可选：

    Add(X, Y, Z = 0)
    {
       return X + Y + Z
    }

当调用者传递满三个参数给上面的函数时，Z 的默认值将被忽略。但是当调用者仅传递了两个参数时，Z 自动地获取默认值 0。

不能将可选参数孤立在参数列表的中间。换句话说，位于首个可选参数右边的所有的参数也必须标记为可选。

在 v1.0.46.13 及之后版本中，ByRef 参数也可支持默认值；例如：Func(ByRef p1 = "")。每当调用者省略了这样一个参数，函数会创建一个局部变量并赋上默认值；换句话说，函数会表现得像没有 ByRef 这个关键词一样。

一个参数的默认值必须是下列形式之一：true, false, 一个原义的整数，一个原义的浮点数，或一个引用的/原义的字符串例如 "fox" 或 "" (但在 1.0.46.13+ 之前的版本只允许 "")。

## 局部变量和全局变量

#### 局部变量

所有在函数内部引用或创建的变量默认都是局部的（除了 Clipboard, ErrorLevel 和 A_TimeIdle 这些内置变量）。每个局部变量的内容都只能在函数内可见。所以，一个局部变量可以和一个全局变量有着相同的名字却有着不同的内容。最后，所有的局部变量每次在函数被调用时都以空值开始。

#### 全局变量

要在一个函数里引用一个存在的全局变量(或创建一个新的)，需要在使用它之前先声明此变量为 global。例如：

    LogToFile(TextToLog)
    {
     global LogFileName ;这个全局变量先前已经在此函数外赋过值了。
     FileAppend, %TextToLog%`n, %LogFileName%
    }

如果一个函数需要引用或创建大量的全局变量，可以通过将它的首行设为单词"global"或者声明一个局部变量来假设它所有的变量都是全局的(它的参数除外)。例如：

    SetDefaults()
    {
     global ;如果在此函数的首行有比如"local MyVar"这样的词，那么这个单词可以被省略。
     Var := 33 ;将 33 赋值给一个全局变量，如果需要，首次可创建变量。
     local x, y:=0, z ;局部变量必须用这种形式来声明，不然它们会被假定为全局变量。
      ; ...等等。
    }

这种假设的全局模式也可以被函数用来创建一个全局数组，例如一个赋值给 Array%A_Index% 的循环。

#### 静态变量

一个变量可以被声明为 static 来使它的值在多次调用期间被记住。例如：

    LogToFile(TextToLog)
    {
     static LineCount = 0
     LineCount += 1 ;保持自身的累加(它的值在多次调用期间能被记住)。
     global LogFileName
     FileAppend, %LineCount%: %TextToLog%`n, %LogFileName%
    }

静态变量一般都是隐式的局部变量。在 1.0.46 之前的版本，所有的静态变量都以空值开始；所以要检查一个静态变量首次被使用的唯一办法就是检查它是否为空值。在 v1.0.46 及之后的版本，一个静态变量可以初始化为除了 "" 外的东西，通过其后跟 := 或 = 以及下列之一：true, false, 一个原义的整数，一个原义的浮点数，或一个引用的/原义的字符串比如 "fox"。例如: static X:=0, Y:="fox"。每个静态变量都只初始化一次(在脚本执行之前)。

#### 关于局部和全局的更多信息：

在下面例子中，通过逗号将多种变量分隔开从而在同一行声明：

    global LogFileName, MaxRetries := 5
    static TotalAttempts = 0, PrevResult

在 v1.0.46 及之后版本，一个局部或者全局变量可以在同一行被初始化，通过在它的声明后加上 := 或 = 以及任何表达式(在声明中运算符 = 和 := 作用相同)。在一个特定行有多个声明时，由于性能原因(不像普通的逗号分隔语句)每个有初始化设定的声明都被作为单独的行执行。与静态变量初始化设定不同，局部变量和全局变量的初始化设定在每次函数被调用时都会执行，但是仅当控制流实际能到达它们这时。换句话说，在一行中写下 local x = 0 和在两个单独的行写下 local x 以及 x = 0 的作用是一样的。

因为单词 local, global 和 static 在脚本启动时会被立即处理，所以不能用 IF 语句来有条件地声明变量。换句话说，在一个 IF 或 ELSE 区块里的声明，声明与函数末尾大括号之间的所有行都会无条件地生效。同时注意目前还不可能声明一个动态变量比如 global Array%i%。

在一个函数里，任何动态变量引用比如 Array%i% 常常会解析为一个局部变量，除非那个名称的变量不存在，在这时才会使用全局变量如果它存在的话。如果两者都不存在，那么需要变量先被创建才能使用，它会被创建为一个局部变量，除非假设全局模式已生效。因此，仅当函数被定义为假设全局函数时，它才能够手动地创建一个全局数组(比如使用 Array%i% := A_Index 这样的方法)。

对于创建数组的命令(例如 StringSplit)，如果假设全局模式未生效或者数组的首个元素已声明为局部变量(将函数的一个参数传递给它也可以 -- 即使那个参数是 ByRef 的 -- 因为参数和局部变量很相似)，那么得出的数组就是局部的。相反的，如果首个元素已被声明为全局，那么创建的是全局数组。StringSplit 的首个元素是 ArrayName0。对于其他创建数组的命令比如 WinGet List，首个元素是 ArrayName(即没有数字)。

常见疑点：在脚本启动时对变量的任何非动态引用都将创建那个变量。例如：在脚本启动的时候，在函数外使用 MsgBox %Array1% 将创建全局的 Array1。相反的，在脚本启动时，在函数内部使用 MsgBox %Array1% 将创建 Array1 作为函数局部变量之一(除非假设全局已生效)。

## 优化布尔求值

当在表达式中使用 AND, OR 和三元运算符时，他们为提高性能而优化(不管当前是否有函数调用)。通过拒绝计算一个表达式里那些不能影响它的最终结果的部分来实行优化操作。要阐明此观点，请看下例:

    if (ColorName <> "" AND not FindColor(ColorName))
       MsgBox 没找到 %ColorName%。

在上例中，如果 ColorName 变量为空，FindColor() 函数永远不会被调用。这是因为 AND 左侧的结果将为 false，因此它的右侧不可能让最终结果输出为 true。

由于此特性，所以必须明白，如果在 AND 或 OR 右侧调用函数，函数可能永远不会产生任何副作用(例如改变一个全局变量的内容)。

同时也要注意在嵌套的 AND 和 OR 中串联的求值优化。例如，在下面的表达式里，每当 ColorName 为空，就只需最左侧的比较就能执行了。这是因为左侧的表达式已经能确保最终的结果：

    if (ColorName = "" OR FindColor(ColorName, Region1) OR FindColor(ColorName, Region2))
     break ;搜索内容为空，或者已经有一个匹配。

从上面例子来看，任何耗时的函数一般都应该在 AND 或 OR 的右侧调用从而提高性能。这个技术还能用来阻止函数的某个参数在传递一个不恰当的值比如一个空字符串时函数被调用。

在 v1.0.46 及之后的版本，三重条件运算符 (?:) 也通过不计算丢失的分支来优化求值。

## 在函数中使用子程序

尽管一个函数不能包含其他函数的定义，但它可以包含子程序。与其他子程序一样，可使用 Gosub 来启动它们，Return 来返回结果(这时候 Return 属于 Gosub 而不是函数)。

已知限制：当前，在整个脚本中每个子程序的名称(标签)必须是独一无二的。如果存在重复的标签，程序将会通知你。

如果一个函数使用 Gosub 跳转到一个公共子程序（在函数括号以外的子程序），那么在外部的所有变量都是全局变量，而函数自己的局部变量在子程序返回之前将不可用。

虽然一般不鼓励使用 Goto 命令，但是它能用来在同个函数内跳转到函数的其他位置。这能帮助我们简化有很多返回点的复杂函数，所有那些返回点需要在返回之前做一些清理。

尽管 Goto 那些函数外部的目标被忽略，但是对函数来说可以用 Gosub 进入一个外部的/公用的子程序然后从那里使用 Goto。

一个函数可以包含从外部调用的子程序，比如定时器, GUI 和菜单项。通常将它们封装在不同的文件中供 #Include 使用，这将防止它们干扰脚本的自动执行部分。不过，还有如下限制:

- 如果它们的函数是正常地调用，这样的子程序只能用静态变量和全局变量(不能是局部变量)。这是因为一个子程序线程打断一个函数调用线程(或者反过来也一样)将能够改变被打断的线程看到的局部变量的值。此外，每当函数返回它的调用者时，它所有的局部变量都被清空从而释放它们的内存。
- 这样的子程序应该只将全局变量(不是静态变量)用作 GUI 控件变量。
- 当一个子程序线程进入一个函数时，任何由此线程引用的动态变量将被作为全局变量对待(包括创建数组的命令)。

## Return, Exit 和一般说明

如果一个函数内部的执行流在到达函数的结束大括号之前遇上一个 Return，那么函数将结束并返回一个空值(空字符串)给它的调用者。每当函数明确地省略 Return 的参数时也将返回一个空值。

当一个函数使用 Exit 命令来结束当前线程的时候,它的调用者根本不会收到返回值。例如：在语句 Var := Add(2, 3) 中如果 Add() 退出，那么 Var 将不做改变。如果函数遇上运行错误比如运行了一个不存在的文件(当 UseErrorLevel 无效时)，那么也将发生同样的事情。

一个函数可以改变 ErrorLevel 的值为了返回一个更容易记忆的额外值。

要在调用函数时使用一个或多个空值(空字符串)，可以像这个例子一样使用一对空的双引号: FindColor(ColorName, "")

因为调用函数不会启用新的线程，所以函数做出的任何设置比如 SendMode 和 SetTitleMatchMode 的改变也都会影响到它的调用者。

一个函数的调用者可能传递一个不存在的变量或者数组元素给它，这在函数期望这个相应的参数被 ByRef 时将变得很有用。例如：调用GetNextLine(BlankArray%i%) 将自动地创建一个局部或全局变量 BlankArray%i% (根据调用者是否在函数内并且是否有假设的全局模式在起作用)。

当在一个函数里使用时，ListVars 会显示一个函数的局部变量和它们的内容。这能帮助我们调试脚本。

## 风格和命名规则

你会发现如果给复杂的函数的特定变量加一个独特的前缀，将使函数更易于阅读和维护。例如，用 "p" 或 "p_" 开头来命名函数的每个参数能让你一眼就辨别出它们的特性，特别是当函数还有很多局部变量来争着吸引你眼球的时候。相似地，"r" 或 "r_" 可以用在 ByRef 参数的前面，"s" 或 "s_" 可以用在静态变量的前面。

单个正确的大括号(OTB) 类型也可以用来定义函数。例如:

    Add(x, y) {
        return x + y
    }

## 用 #Include 在多个脚本中共享函数

可以用 #Include 指令(甚至在脚本的顶部)从外部文件加载函数。

说明：当脚本的执行流遇到函数定义时，它会跳过函数(用一种瞬间完成的方法)，并在它的结束大括号的下一行恢复执行。所以执行流不会从上面掉进一个函数，也不会因为在脚本的顶部有一个或多个函数而影响到自动执行部分。

## 函数库：标准库和用户库 [v1.0.47+]

一个脚本不一定非要使用 #Include 来调用外部文件中的函数。要达到这个目的，一个与函数同名的文件必须存在于下列的库目录之一中：

    %A_MyDocuments%\AutoHotkey\Lib\ ;用户库。此目录是可选的；也可以完全不存在。
    当前运行的 AutoHotkey.exe 的路径\Lib\ ;标准库。同样是可选的。

例如，假设一个脚本调用一个不存在的函数 MyFunc()，程序将在用户库里搜索名为 MyFunc.ahk 的文件。如果找不到，它将在标准库里继续搜索。如果仍未找到并且函数的名字里有一个下划线 (比如 MyPrefix_MyFunc)，那么程序将在两个库里搜索名为 MyPrefix.ahk 的文件，如果存在的话会加载它。这使得 MyPrefix.ahk 可以包含函数 MyPrefix_MyFunc 以及其他相关的函数名以 MyPrefix_ 开头的函数。

虽然一个库文件通常只包含一个和它的文件名同名的函数，但它也可以包含仅被同名函数调用的私有函数和子程序。然而，这些函数应该具有相当独特的名称，因为它们仍会在全局命名空间里；也就是说，可以从脚本的任意位置调用它们。

如果一个库函数使用 #Include，那么 #Include 的工作目录就是库函数自身所在的目录。这能被用来创建一个重定向到一个包含此函数和其他与之相关的函数的巨大的库文件。

脚本编译器 (ahk2exe)同样支持库函数。不过，它需要编译器目录的上层目录里存在一个复制的 AutoHotkey.exe(通常已是如此)。如果 AutoHotkey.exe 不存在，编译器仍可以运行，不过无法自动地包含库函数。

包含一个库函数也会执行地同其他函数一样好，因为它们在脚本开始执行之前已经被预加载了。

## 内置函数

在一个内置函数的参数列表末尾的任何可选的参数是可以被完全省略的。例如，WinExist("Untitled - Notepad") 是有效的，因为它的另外三个参数将被视为空。

如果脚本定义了一个和内置函数同名的函数，内置函数将被覆盖。例如：一个脚本将调用它自定义的 WinExist() 函数来代替标准的那个。

存在于 DLL 文件里的外部函数可以通过 DllCall() 来调用。

### 经常使用的函数

FileExist(FilePattern): 如果 FilePattern 不存在(如果未指定绝对路径，程序将假设 FilePattern 在 A_WorkingDir )就返回一个空值(空字符串)。否则，它将返回首个匹配的文件或文件夹的属性字串("RASHNDOCT"的子集) 。如果文件没有属性(罕见)，将返回 "X"。FilePattern 可以是文件或文件夹的准确的名称，或者包含通配符 (* 或 ?)。因为空字符串被视为 "false"，所以函数的返回值总是可以作为一个准布尔值来使用。例如：如果文件存在，语句 if FileExist("C:\My File.txt") 将为 true，反之为 false。相似地，只有当文档存在并且是一个目录，语句 if InStr(FileExist("C:\My Folder"), "D") 才为 true。相关命令: IfExist 和 FileGetAttrib。

GetKeyState(KeyName [, "P" or "T"]): 与 GetKeyState 命令不同，后者是按下的返回 D，弹起的返回 U；而这个函数是如果键是按下的就返回 true (1)，弹起的返回 false (0)。如果参数 KeyName 是无效的，将返回空字符串。请看 GetKeyState 来了解其他返回值和用法。

InStr(Haystack, Needle [, CaseSensitive = false, StartingPos = 1]): 返回 Haystack 字符串中首个匹配的 Needle 字符串的位置。与 StringGetPos 不同，第一个字符的位置是 1；这是因为 0 同义于 "false"，会使其成为一个直观的”未找到“的指示。如果参数 CaseSensitive 被省略或者是 false，则搜索将不区分大小写(不区分的模式取决于 StringCaseSense 命令)；否则就得精确地匹配大小写。如果省略 StartingPos，其默认为 1(Haystack 字符串的起点)。否则，指定 2 从 Haystack 的第二个字符开始搜索，3 从第三个开始，等等。如果 StartingPos 超出 Haystack 的长度，那么函数将返回 0。如果 StartingPos 为 0，那么搜索将逆序执行(从右到左)以便找到在最右边匹配的结果。不管 StartingPos 的值是多少，返回的位置将始终相对于 Haystack 的首个字符而言。例如：在"123abc789"中"abc"的位置永远是 4。相关链接：RegExMatch(), IfInString 和 StringGetPos。（译注：例如 InStr("123abc789","abc",false,1)）

RegExMatch(Haystack, NeedleRegEx [, UnquotedOutputVar = "", StartingPos = 1]): 请看 RegExMatch()。

RegExReplace(Haystack, NeedleRegEx [, Replacement = "", OutputVarCount = "", Limit = -1, StartingPos = 1]): 请看 RegExReplace()。

SubStr(String, StartingPos [, Length]) [v1.0.46+]: 在 String 字符串中从 StartingPos 起始点开始向右复制不超过 Length 长度的字符的子字符串(如果参数 Length 省略，就默认为“所有字符”)。对于 StartingPos，指定 1 则从首个字符开始，2 则从第二个字符开始，以此类推(如果 StartingPos 超出了 String 的长度，将返回一个空字符串)。如果 StartingPos 小于 1，将被视为从字符串末尾开始的位移。例如，0 提取最后一个字符，-1 提取最后两个字符(但是如果 StartingPos 超过了字符串的左侧末尾，提取又会从左侧首个字符开始)。Length 是要获取的字符的最大数目(每当字符串剩余部分太短的时候，获取的长度会比最大数目少一些)。指定一个负的 Length 从而在返回的字符串的末尾省略这么多个字符(如果省略了全部或更多字符，将返回一个空字符串)。相关链接：RegExMatch(), StringMid, StringLeft/Right, StringTrimLeft/Right。

StrLen(String): 返回 String 的长度。如果 String 是 ClipboardAll 先前分配的变量，将返回它的总大小。相关命令：StringLen。

WinActive([WinTitle, WinText, ExcludeTitle, ExcludeText]): 如果激活的窗口匹配指定的条件，则返回窗口的唯一 ID (HWND)。如果不匹配，函数返回 0。因为所有非零的值都视为 "true"，所以每当匹配了 WinTitle 的窗口被激活时语句 if WinActive("WinTitle") 都是 true。最后，WinTitle 支持 ahk_id，ahk_class 以及其他特殊字符串。关于这些和窗口激活方面的其他信息，详见 IfWinActive。

WinExist([WinTitle, WinText, ExcludeTitle, ExcludeText]): 返回以十六进制整数表示的首个匹配窗口的唯一 ID (HWND)(如果没有就是 0)。因为所有非零的值都视为 "true"，所以每当匹配了 WinTitle 的窗口存在时语句 if WinExist("WinTitle") 都是 true。最后，WinTitle 支持 ahk_id, ahk_class 以及其他特殊字符串。关于这些和窗口查找方面的其他信息，详见 IfWinExist。

### 杂项函数

Asc(String): 返回 String 里首个字符的 ASCII 码(一个1 到 255 之间的数字)。如果 String 为空，则返回 0。

Chr(Number): 根据 Number 返回 ASCII 码里相应的单个字符。如果 Number 不是 1 和 255 及其之间的数，将返回一个空字符串。常用的 ASCII 代码包括 9 (tab), 10 (换行), 13 (回车), 32 (空格), 48-57 (数字 0-9), 65-90 (大写字母 A-Z) 和 97-122 (小写字母 a-z)。

DllCall(): 请看 DllCall()。

IsLabel(LabelName): 如果 LabelName 是脚本中子程序, 热键或热字符串的标签名称(在 LabelName 中不要包括尾部的冒号)，则返回一个非零值。例如：如果标签存在，语句 if IsLabel(VarContainingLabelName) 将为 true，反之就是 false。当你在 Gosub, Hotkey, Menu 和 Gui 这样的命令中指定了一个动态标签时，这个函数对避免运行错误将十分有用。

ListView 和 TreeView 函数: 详见 ListView 和 TreeView 页面。

NumGet(VarOrAddress [, Offset = 0, Type = "UInt"]) [v1.0.47+]: 返回储存在指定地址+偏移量中的二进制数。对于 VarOraddress，传递 MyVar 相当于传递 &MyVar。不过，省略 "&" 可以执行地更好并确保目标地址是有效的（无效的地址将返回 ""）。相比之下，除了变量，传递给 VarOrAddress 的其他任何东西都被当作原始地址；因此，指定 MyVar+0 会强制用 MyVar 中的数字来代替 MyVar 本身的地址。对于 Type，可以指定为 UInt, Int, Int64, Short, UShort, Char, UChar, Double 或者 Float(不过与 DllCall 不同，当这些作为原义字符串使用时必须被括在引号里)；详见 DllCall Types。

NumPut(Number, VarOrAddress [, Offset = 0, Type = "UInt"]) [v1.0.47+]: 在指定地址+偏移量中以二进制的格式储存 Number ，并在刚刚写入的项目的右边返回地址。对于 VarOrAddress，传递 MyVar 相当于传递 &MyVar。不过，省略 "&" 可以执行地更好并确保目标地址是有效的（无效的地址将返回 ""）。相比之下，除了变量，传递给 VarOrAddress 的其他任何东西都被当作原始地址；因此，指定 MyVar+0 会强制用 MyVar 中的数字来代替 MyVar 本身的地址。对于 Type，可以指定为 UInt, Int, Int64, Short, UShort, Char, UChar, Double 或者 Float(不过与 DllCall 不同，当这些作为原义字符串使用时必须被括在引号里)；详见 DllCall Types。如果一个整数太大而无法适应指定的 Type，它最重要的字节就被忽略了；比如 NumPut(257, var, 0, "Char") 将存储数字 1。

OnMessage(MsgNumber [, "FunctionName"]): 监控消息/事件。详见 OnMessage()。

RegisterCallback(): 请看 RegisterCallback()。

VarSetCapacity(UnquotedVarName [, RequestedCapacity, FillByte]): 扩大一个变量的容积或者释放它的内存。详见 VarSetCapacity()。

### 常用数学函数

注意：如果传入的任何参数是非数值的，那么数学函数通常会返回一个空值(空字符串)。

Abs(Number): 返回 Number 的绝对值。返回值的类型和 Number 一致(整数或浮点数)。

Ceil(Number): 返回 Number 向上取整数(没有任何的 .00 后缀)的值。例如，Ceil(1.2) 是 2，Ceil(-1.2) 是 -1。

Exp(N): 返回 e (大约为 2.71828182845905)的 N 次幂。N 可以是负数也可以包含小数点。要抬升除了 e 以外的数到某个幂，请用 ** 运算符。

Floor(Number): 返回 Number 向下取整数(没有任何的 .00 后缀)的值。比如 Floor(1.2) 是 1，Floor(-1.2) 是 -2。

Log(Number): 返回 Number 的对数(以 10 为底)。结果被格式化为浮点数。如果 Number 是负数，将返回一个空字符串。

Ln(Number): 返回 Number 的自然对数(以 e 为底)。结果被格式化为浮点数。如果 Number 是负数，将返回一个空字符串。

Mod(Dividend, Divisor): 求模。返回被除数 Dividend 除以除数 Divisor 时余下的值。结果的正负号和被除数一致。例如：mod(5, 3) 和 mod(5, -3) 都得出 2，但是 mod(-5, 3) 和 mod(-5, -3) 得出 -2。如果输入的任何一个是浮点数，那么结果也会是浮点数。例如，mod(5.0, 3) 结果为 2.0，mod(5, 3.5) 结果为 1.5。如果除数为零，则函数得到空值(空字符串)。

Round(Number [, N]): 如果 N 为 0 或者省略 N， Number 将四舍五入为整数。如果 N 是正数，Number 取 N 个小数位。如果 N 是负数，在 Number 的小数点的左边取 N 位来四舍五入。例如：Round(345, -1) 结果为 350，Round(345, -2) 结果为 300。与 Transform Round 不同，每当 N 小于 1 或者省略时，结果没有 .000 后缀。在 v1.0.44.01 及之后的版本中，一个大于零的 N 值会确切地显示 N 个小数位，而不会服从 SetFormat。要避免这种情况的话，对 Round() 的返回值再执行另一个数学操作；例如：Round(3.333, 1)+0。

Sqrt(Number): 返回 Number 的平方根。结果被格式化为浮点数。如果 Number 是负数，函数将得出空值(空字符串)。

### 三角函数

Sin(Number) | Cos(Number) | Tan(Number): 返回 Number 的正弦|余弦|正切三角函数值。Number 必须用弧度表示(详见下面)。

ASin(Number): 返回以弧度表示的反正弦(其正弦是 Number)。如果 Number 小于 -1 或者大于 1，则函数得到一个空值(空字符串)。

ACos(Number): 返回以弧度表示的反余弦(其余弦是 Number)。如果 Number 小于 -1 或者大于 1，则函数得到一个空值(空字符串)。

ATan(Number): 返回以弧度表示的反正切(其正切是 Number)。

注意：要将弧度转换成角度，将其乘以 180/pi (大约为 57.29578)。要将角度转换成弧度，将其乘以 pi/180 (大约为 0.01745329252)。pi (大约为 3.141592653589793)的值是 1 的反正切乘以 4。

### 其他函数

Titan 的命令函数: 为每个有 OutputVar 的 AutoHotKey 命令提供一个可调用的函数。可以通过 #Include 将这个库包含在任何一个脚本里。

翻译：hsudatalks 修正：天堂之门 menk33@163.com 2008年11月10日