Skip to content

列表 / 统计函数

列表函数是 IM 脚本里最容易出坑的部分——list_get 返回深拷贝。本章把读取、修改、统计三类函数放在一起,并集中讲深拷贝的正确姿势。

列表读取

函数参数返回说明
list_size(list)ListInteger元素个数
list.size()Integer方法形式,等价于 list_size
list.length()Integer最大有效下标size - 1);空列表返回 -1
length(list)ListIntegerlist.length()(v1.3.0+)
list_get(list, index)List, IntegerAny取第 index 个元素(支持负索引,越界抛错)
list_contains(list, item)List, AnyBoolean是否包含
list_join(list, sep)List, StringString拼接为字符串
list_index(list, value)List, AnyInteger查找值的索引,不存在返回 -1

size vs length

  • size() / list_size() 返回元素个数。空列表返回 0
  • length() 返回最大有效下标size - 1)。空列表返回 -1

循环边界时大多数情况都用 size()for i in 0..list_size(arr) - 1

负索引

索引含义
0第一个元素
-1最后一个元素
-2倒数第二个
yaml
arr = [10, 20, 30, 40, 50]
list_get(arr, 0)     # 10
list_get(arr, -1)    # 50
list_get(arr, -2)    # 40

列表修改

所有修改函数都返回新列表,原列表不变。必须用 = list_xxx(...) 接住返回值。

函数参数返回说明
list_add(list, item)List, AnyList末尾追加
list_insert(list, idx, item)List, Integer, AnyList插入到指定位置
list_set(list, idx, item)List, Integer, AnyList替换第 idx
list_remove(list, idx)List, IntegerList删除第 idx
list_remove_value(list, value)List, AnyList删除第一个匹配值
list_clear(list)ListList清空(返回空列表)
list_copy(list)ListList深拷贝
yaml
arr = [1, 2, 3]
arr = list_add(arr, 4)               # [1, 2, 3, 4]
arr = list_insert(arr, 1, 99)        # [1, 99, 2, 3, 4]
arr = list_set(arr, 0, 100)          # [100, 99, 2, 3, 4]
arr = list_remove(arr, 1)            # [100, 2, 3, 4]
arr = list_remove_value(arr, 3)      # [100, 2, 4]
副本 = list_copy(arr)                # 完全独立的副本

list_get 的深拷贝陷阱

对 ItemStack 元素,list_get() 返回 .clone() 深拷贝。对副本的任何修改都不会影响原列表,也不会影响原始变量。

yaml
# ❌ 典型错误
材料列表 = [材料1, 材料2, 材料3]
数量列表 = [10, 20, 30]
for i in 1..材料列表.size() {
  副本 = list_get(材料列表, i-1)       # 深拷贝!
  副本.lore.append("&7需要: {数量}")   # 修改的是副本
}
slot.set("s1", 材料1)                  # ❌ 材料1 没变

三种正确模式:

yaml
# ✓ 模式 1:用 list_set 写回列表
for i in 1..材料列表.size() {
  副本 = list_get(材料列表, i-1)
  副本.lore.append("&7需要: 10")
  材料列表 = list_set(材料列表, i-1, 副本)
}
slot.set("s1", list_get(材料列表, 0))

# ✓ 模式 2:直接用修改后的副本
for i in 1..材料列表.size() {
  副本 = list_get(材料列表, i-1)
  副本.lore.append("&7需要: 10")
  slot.set("s" + i, 副本)
}

# ✓ 模式 3:不用列表中转,直接操作原变量
材料1.lore.append("&7需要: 10")
材料2.lore.append("&7需要: 20")
slot.set("s1", 材料1)
slot.set("s2", 材料2)

列表存的是值,不是变量名

yaml
物理攻击 = 1.5
生命力 = 4.5

# ❌ 你以为存的是 ["物理攻击", "生命力"]
属性列表 = [物理攻击, 生命力]
# 实际存的是 [1.5, 4.5]

for i in 1..属性列表.size() {
  属性 = list_get(属性列表, i-1)
  lore.replace("{属性}", "旧", "新")
  # 执行的是 lore.replace("1.5", ...),找不到含 "1.5" 的行
}

# ✓ 用字符串字面量
属性名列表 = ["物理攻击", "生命力"]
旧值列表 = [当前攻击, 当前生命]
新值列表 = [新攻击, 新生命]

for i in 1..属性名列表.size() {
  属性名 = list_get(属性名列表, i-1)
  旧值 = list_get(旧值列表, i-1)
  新值 = list_get(新值列表, i-1)
  lore.replace("{属性名}", "{旧值}", "{新值}")
}

槽位组的列表操作

槽位组(multiple=true)在脚本里就是 List<ItemStack>,空位为 null

yaml
slots:
  F: input(id=food, multiple=true, match="lore.contains('可被吞噬')")

execute: |
  饲料数 = count_filled(@food)           # 有物品的个数
  for i in 0..list_size(@food)-1 {
    物品 = list_get(@food, i)
    if 物品 != null {
      log("第", i, "格:", 物品.name)
    }
  }

统计函数

函数参数返回说明
count_filled(slots...)槽位引用...Integer有物品的槽位数(支持单个和槽位组)
count_on(toggles...)开关引用...Integer开启的开关数
sum(values...)Number...Number求和
sum_extract(items, pattern, group)List<Item>, 正则, IntegerNumber从 Lore 正则提取数值求和
sum_attr(items, attrName)List<Item>, StringNumber累加多物品的属性值
yaml
# 槽位组统计
已放入 = count_filled(@food)
总攻 = sum_attr(@food, "攻击力")
总伤 = sum_extract(@food, "伤害:\\s*\\+(\\d+)", 1)

# 开关统计
开启数 = count_on(@lock1, @lock2, @lock3)

# 普通数值求和
总数 = sum(a, b, c, d)

sum_attr 等价于"先用 @input.attr() 逐个取值再求和",但写法简洁得多,优先用它代替 for 循环

何时避免 list_get / for

  • 累加属性:用 sum_attr(@food, "攻击力"),不要 for 循环。
  • 统计非空槽位:用 count_filled,不要手动循环判断。
  • 检查列表元素:用 list_contains,不要 for 循环比较。
  • Lore 批量替换:用 lore.random_replace / lore.sort,详见 Lore 动作

TQ Minecraft Server Plugin Docs