Repository navigation
Lambda
到目前为止,你写的每个函数都有一个名字。Lambda 是没有名字的函数——一段可以被传来传去的代码。它最常见的用途是给 GUI 按钮和事件处理器注册回调。
Every function you have written so far has a name. A lambda is a function without a name — a block of code you can pass around. Its most common use is registering callbacks for GUI buttons and event handlers.
最简单的 lambda:
The simplest lambda:
var f = (int x) -> x * 2;
int y = f.apply(5); // 10
这里 (int x) -> x * 2 是一个 lambda:接收一个整数,返回它的两倍。f 是一个变量,持有这个 lambda。f.apply(5) 调用它。
Here (int x) -> x * 2 is a lambda: takes an integer, returns twice it. f is a variable holding the lambda. f.apply(5) invokes it.
Lambda 有两种写法:
Two syntactic forms:
// 单参数:可以省略括号和类型
x -> x * 2
// 多参数:括号,逗号分隔,类型可省
(a, b) -> a + b
完整的写法带类型声明:
Full form with type declarations:
(int x) -> x * 2
(int a, int b) -> a + b
省略类型时,HimDSL 会根据上下文推断。推断不出来时会报错,这时需要补上类型。
Types may be omitted when the context can infer them. If inference fails, add them back.
箭头右侧可以是一个表达式:
The right side of the arrow may be an expression:
var square = (int x) -> x * x;
也可以是一个语句块(用花括号):
Or a block (braces):
var log = (string msg) -> {
string line = "[INFO] " + msg;
runcmd("CONSOLE", "say " + line);
};
块体适合逻辑超过一行的情况。表达式体适合一行就能写完的计算。
Block bodies suit multi-line logic. Expression bodies suit one-liners.
块体需要返回时,必须显式写 return:
A block body must write return explicitly to produce a value:
var safeDiv = (int a, int b) -> {
if (b == 0) {
return 0;
}
return a / b;
};
表达式体不需要 return,箭头右侧的值就是返回值。
Expression bodies need no return; the value on the right is the result.
和数字、字符串一样,lambda 可以:
Like numbers and strings, a lambda can be:
-
存进变量。
-
Stored in a variable.
var f = (int x) -> x + 1;
-
作为参数传给其他函数。
-
Passed as a function argument.
void applyTwice(var f, int x) {
int y = f.apply(x);
int z = f.apply(y);
runcmd("CONSOLE", "say " + z);
}
int main() {
applyTwice((int v) -> v * 3, 2); // ((2*3)*3) = 18
}
-
作为函数的返回值。
-
Returned from a function.
var makeAdder(int n) {
return (int x) -> x + n;
}
int main() {
var add5 = makeAdder(5);
runcmd("CONSOLE", "say " + add5.apply(10)); // 15
}
最后这个例子体现了 lambda 的闭包能力:它记住了定义时周围的变量 n。即使 makeAdder 已经结束,lambda 依然能用 n 的值。
The last example shows closure: the lambda remembers n from its defining scope. Even after makeAdder returns, the lambda still uses n.
调用 lambda 使用 .apply(...):
Invoke with .apply(...):
var f = (int a, int b) -> a + b;
int result = f.apply(3, 4); // 7
无参数的 lambda:
A lambda with no parameters:
var hello = () -> runcmd("CONSOLE", "say hello");
hello.apply();
块体无返回值时,apply 返回 null。
When the block body returns nothing, apply returns null.
Lambda 能读取定义位置可见的所有变量:
A lambda reads every variable visible at its definition site:
int base = 100;
var addBase = (int x) -> x + base;
int main() {
runcmd("CONSOLE", "say " + addBase.apply(5)); // 105
}
注意 lambda 捕获的是变量,不是值。如果捕获的变量后来被修改,lambda 看到的是最新的值:
Note that lambdas capture variables, not values. If the captured variable is later modified, the lambda sees the latest value:
int counter = 0;
var bump = () -> {
counter = counter + 1;
return counter;
};
int main() {
runcmd("CONSOLE", "say " + bump.apply()); // 1
runcmd("CONSOLE", "say " + bump.apply()); // 2
runcmd("CONSOLE", "say " + bump.apply()); // 3
}
Lambda 内部对 counter 的修改会影响外层。用这一点可以实现"计数器"之类的效果,但也要小心意外的副作用。
Modifications to captured variables propagate outward. This enables "counters" but also risks unintended side effects.
Lambda 最常见的应用是给 GUI 槽位注册点击回调:
The most common lambda use is registering GUI click callbacks:
int main() {
var gui = new GUI(1, "菜单");
var slot = new GUISlot(Material.DIAMOND);
slot.name = "点击领奖";
slot.onLeftClick = () -> {
player p = @s;
runcmd(p.name, "give " + p.name + " diamond 1");
};
gui[0][4] = slot;
gui.updateAll();
}
这里的 () -> { ... } 是点击时执行的代码。@s 在回调里可用,指向点击的玩家。
Here () -> { ... } is the code executed on click. @s works inside the callback, pointing at the clicking player.
(GUI 的完整用法在后面章节,这里只需要感受 lambda 的形状。)
(Full GUI usage is later; here we only need to feel the shape of a lambda.)
Lambda 和具名函数做的是同一件事,区别在于有没有名字:
A lambda and a named function do the same thing; the difference is whether they have a name:
// 具名函数
int doubleIt(int x) { return x * 2; }
// 等价 lambda
var doubleIt2 = (int x) -> x * 2;
调用方式不同:具名函数直接 doubleIt(5);lambda 用 doubleIt2.apply(5)。
The call syntax differs: a named function is invoked as doubleIt(5); a lambda as doubleIt2.apply(5).
如果一个逻辑只在一处使用,用具名函数更清楚。只有当它要被"传出去"——作为参数、返回值、回调——lambda 才体现价值。
If a piece of logic is used in one place, a named function is clearer. A lambda earns its keep only when passed around — as an argument, a return value, or a callback.
-
Lambda 是无名函数,用
->定义。 -
表达式体一行,块体多行。
-
用
.apply(...)调用。 -
Lambda 是值,可以存、传、返回。
-
Lambda 捕获定义位置的变量,修改会传回。
-
GUI 点击回调是它的典型应用。
-
只在一处使用时,用具名函数更好。
-
A lambda is a nameless function, defined with
->. -
Expression bodies are one-liners; block bodies are multi-line.
-
Invoke with
.apply(...). -
Lambdas are values: store, pass, return.
-
Lambdas capture variables; writes propagate back.
-
GUI click callbacks are a classic use.
-
For single-use logic, a named function is better.
下一章 [函数式接口 / Functional-Interfaces] 介绍 lambda 与 Java 类型系统的衔接,例如把 lambda 传给 Runnable 之类的接口。
Next, [函数式接口 / Functional-Interfaces] covers how lambdas connect to Java types, such as passing a lambda to Runnable.
- 首页 / Home
- 安装与快速开始 / Installation & Quick Start
- 第一个脚本 / Your First Script
- 命令速查 / Command Reference
- 自动执行脚本 / Auto-run Scripts
- 定义函数 / Defining Functions
- 函数重载 / Function Overloading
- Lambda 表达式 / Lambda Expressions
- 函数式接口 / Functional Interfaces
- 选择器总览 / Selectors Overview
- 选择器过滤器 / Selector Filters
- EntityRef 引用 / EntityRef
- 占位符参考 / Placeholders Reference
- PlaceholderAPI 桥接 / PAPI Bridge
- 创建 GUI / Creating a GUI
- GUISlot 槽位 / GUISlot
- 点击回调 / Click Callbacks
- onClose 回调 / onClose Callback
- GUI 刷新 / GUI Refresh
- 数学函数 / Math Built-ins
- 高精度计算 / BigDecimal Built-ins
- 字符串函数 / String Built-ins
- 集合函数 / Collection Built-ins
- 文件 I/O / File I/O
- 命令与 sleep / Commands & sleep