Carpet编程入坑跳坑指北

作者:禄存
前言
欢迎来TIS论坛,享受极致的 Markdown 渲染体验:https://forum.tis.world/topic/132/carpet编程入坑跳坑指北
首先感谢各位能抽时间看这一篇文章。最近抽了两天时间研究了一下 Carpet 这个脚本语言,这里来和大家分享一下自己的心得体会。
我最早了解到 Carpet 是因为 gnembon 的宣传视频。但是了解了也没去多看。后来因为兔子机研究生物 AI 的时候说到了 Carpet 端,就去研究了一下。花了一个晚上实现了宽度优先搜索,又花了一个下午实现了 A*寻路算法,算是有了点心得体会,来和大家分享一下。

基本介绍
Carpet 是一个用 Java 编写的,拥有较多功能的,基于原版 Minecraft 系统的函数式编程脚本。大体可以用“万物皆函数”概括,与 Java 的“万物皆对象”相映成趣。
Carpet 的运行效率不如Forge,但编写起来更加简单,修改也容易得多。
这里讲到的所有内容都可以在官方的手册 https://gnembon.github.io/scarpet/ 找到。
在读这篇文章之前,最好就对编程有所了解。
语法技巧
首先是各种特殊用法和注意要点:
对列表的一些奇妙操作,这个后面集中讲;
null
的设定是比任何对象都小的:在参与比较大小时,除了与自身相比为true
,否则总是false
;global_
前缀可以声明全局变量,运行完毕后也会被保存;和 Java 一样,
|| &&
是“短路”的,也就是只会在必要条件下检测后一项,例如官方的示例;函数只能传值,例如:
delete(list) -> list = l(); a = l(1,2,3); delete(a); print(a);
的输出是[1, 2, 3]
。
列表,以及字符串
列表是基于 Java 的
ArrayList
的,字符串是基于String
。运行效率如何自己掂量着看;列表可以同时存放不同类型的值,这点跟 Python 有点类似;
列表进行
+ - * /
的时候,如果后一个是数字,就对列表内的每一个对象进行一次操作;如果也是列表并且长度一样,就会将对象一一对应加起来,形成一个新的对象;如果前后列表长度不一样会报错。另外,
l(l(1,2), l(2,3)) + l(l(1,2), l(2,3))
以及l(l(1,2), l(2,3)) + l(1,2)
都是可以的;列表进行
+=
时自动往最后面添加后一个操作数;列表用
element(<list>, <index>)
取出对象的时候,-1 代表最后一个,-2是倒数第二个;下标如果溢出就重新从0开始计数;字符串在使用
+=
+
的时候都会自动将后一个对象转换为字符串然后拼接;~
在对列表使用时,会在列表中查找后一个对象。查到了返回下标,查不到返回null
;对字符串使用时会将后一个对象转换成字符串,作为正则表达式进行匹配。返回匹配到的字符串或者null
;对字符串使用
-
时,会将后一个对象转换成字符串,并返回一个去除了所有该字符串的新字符串,不进行正则匹配,例如'ababa' - 'aba'
返回b
。
函数式编程和列表
抱歉,这里没有点操作符,大括号和中括号也没有
流操作?可以了解,但没卵用
排序列表是 sort(<list>)
但是外部传入的列表对象没变,你需要 list = sort(list)
,之前应该说过了,list.sort()
是什么?不存在的!
不过,你可以在一些作者预定义好的函数里写语句,类似 Java 的 Lambda 表达式。(惨,你自己是没法写能传语句的函数的!)例如这样能把一个列表映射成另一个列表,里面的每一个对应的值都是相反的:
map(list, -_)
它可以当作 Java 里的
list.stream().map(x -> -x).toArray()
这里的下划线_就是list里的每一个对象。
同理可得:
filter(list, _ > 0)
能得到一个只有原列表中正数的新列表。还有高级版的排序:
sort_key(list, -_)
实现了逆序排序。
之前提到了流操作,确实很像,然而并不一样:根本没有那种优化! 例如:first(sort(list), true)
的性能比效果上等价的 min(list)
要差得多!
另外,列表只能在最后添加,不能中间插入,不能中间删除,但是可以根据下标截取出一个新的列表——其他的功能要自己写! 举个例子:
remove_by_val(list, item) -> (
new_list = l();
for (list,
if (item == _,
put(new_list, null, _)
)
);
return(new_list)
);
没错,for
和 if
也是函数,只不过能传入语句。具体要看官方手册的定义,同样的还有 loop
,while
。另外这里没有方便的 break
和 continue
,可能需要立flag或者用超大的条件判断。
看到这里,你可能会觉得不妙:那咋取出列表的特定元素?答案是 element(<list>, <index>)
,element有点奇妙的特性,上文说过。
那关于Minecraft的功能呢???
抱歉,我没怎么了解(笑)
不过看手册,功能还是挺全的,可以控制时间流逝速度,进行随机刻,搜索实体,获取 NBT 标签,生成结构,执行命令,甚至方便地检测一片区域里的方块……毕竟作者在技术社区混了那么久,基本上你想到的需求是一应俱全的。(实时生物AI追踪呢?)
不过也要小心一点。比起命令方块,Carpet 的安全性要差一点。例如一次在聊天栏输出超大的列表会卡死客户端,服务端在执行脚本的时候默认会冻结住之类的……