Carpet編程入坑跳坑指北

作者:祿存
前言
歡迎來TIS論壇,享受極致的 Markdown 渲染體驗(yàn):https://forum.tis.world/topic/132/carpet編程入坑跳坑指北
首先感謝各位能抽時(shí)間看這一篇文章。最近抽了兩天時(shí)間研究了一下 Carpet 這個(gè)腳本語(yǔ)言,這里來和大家分享一下自己的心得體會(huì)。
我最早了解到 Carpet 是因?yàn)?gnembon 的宣傳視頻。但是了解了也沒去多看。后來因?yàn)橥米訖C(jī)研究生物 AI 的時(shí)候說到了 Carpet 端,就去研究了一下。花了一個(gè)晚上實(shí)現(xiàn)了寬度優(yōu)先搜索,又花了一個(gè)下午實(shí)現(xiàn)了 A*尋路算法,算是有了點(diǎn)心得體會(huì),來和大家分享一下。

基本介紹
Carpet 是一個(gè)用 Java 編寫的,擁有較多功能的,基于原版 Minecraft 系統(tǒng)的函數(shù)式編程腳本。大體可以用“萬物皆函數(shù)”概括,與 Java 的“萬物皆對(duì)象”相映成趣。
Carpet 的運(yùn)行效率不如Forge,但編寫起來更加簡(jiǎn)單,修改也容易得多。
這里講到的所有內(nèi)容都可以在官方的手冊(cè) https://gnembon.github.io/scarpet/ 找到。
在讀這篇文章之前,最好就對(duì)編程有所了解。
語(yǔ)法技巧
首先是各種特殊用法和注意要點(diǎn):?
對(duì)列表的一些奇妙操作,這個(gè)后面集中講;
null
?的設(shè)定是比任何對(duì)象都小的:在參與比較大小時(shí),除了與自身相比為?true
,否則總是?false
;global_
?前綴可以聲明全局變量,運(yùn)行完畢后也會(huì)被保存;和 Java 一樣,
|| &&
?是“短路”的,也就是只會(huì)在必要條件下檢測(cè)后一項(xiàng),例如官方的示例;函數(shù)只能傳值,例如:
delete(list) -> list = l(); a = l(1,2,3); delete(a); print(a);
?的輸出是?[1, 2, 3]
。
列表,以及字符串
列表是基于 Java 的?
ArrayList
?的,字符串是基于?String
。運(yùn)行效率如何自己掂量著看;列表可以同時(shí)存放不同類型的值,這點(diǎn)跟 Python 有點(diǎn)類似;
列表進(jìn)行?
+ - * /
?的時(shí)候,如果后一個(gè)是數(shù)字,就對(duì)列表內(nèi)的每一個(gè)對(duì)象進(jìn)行一次操作;如果也是列表并且長(zhǎng)度一樣,就會(huì)將對(duì)象一一對(duì)應(yīng)加起來,形成一個(gè)新的對(duì)象;如果前后列表長(zhǎng)度不一樣會(huì)報(bào)錯(cuò)。另外,
l(l(1,2), l(2,3)) + l(l(1,2), l(2,3))
?以及?l(l(1,2), l(2,3)) + l(1,2)
都是可以的;列表進(jìn)行?
+=
?時(shí)自動(dòng)往最后面添加后一個(gè)操作數(shù);列表用?
element(<list>, <index>)
?取出對(duì)象的時(shí)候,-1 代表最后一個(gè),-2是倒數(shù)第二個(gè);下標(biāo)如果溢出就重新從0開始計(jì)數(shù);字符串在使用?
+=
?+
?的時(shí)候都會(huì)自動(dòng)將后一個(gè)對(duì)象轉(zhuǎn)換為字符串然后拼接;~
?在對(duì)列表使用時(shí),會(huì)在列表中查找后一個(gè)對(duì)象。查到了返回下標(biāo),查不到返回?null
;對(duì)字符串使用時(shí)會(huì)將后一個(gè)對(duì)象轉(zhuǎn)換成字符串,作為正則表達(dá)式進(jìn)行匹配。返回匹配到的字符串或者?null
;對(duì)字符串使用?
-
?時(shí),會(huì)將后一個(gè)對(duì)象轉(zhuǎn)換成字符串,并返回一個(gè)去除了所有該字符串的新字符串,不進(jìn)行正則匹配,例如?'ababa' - 'aba'
?返回?b
。
函數(shù)式編程和列表
抱歉,這里沒有點(diǎn)操作符,大括號(hào)和中括號(hào)也沒有
流操作?可以了解,但沒卵用
排序列表是?sort(<list>)
?但是外部傳入的列表對(duì)象沒變,你需要?list = sort(list)
,之前應(yīng)該說過了,list.sort()
?是什么?不存在的!
不過,你可以在一些作者預(yù)定義好的函數(shù)里寫語(yǔ)句,類似 Java 的 Lambda 表達(dá)式。(慘,你自己是沒法寫能傳語(yǔ)句的函數(shù)的!)例如這樣能把一個(gè)列表映射成另一個(gè)列表,里面的每一個(gè)對(duì)應(yīng)的值都是相反的:
map(list, -_)
它可以當(dāng)作 Java 里的
list.stream().map(x -> -x).toArray()
這里的下劃線_就是list里的每一個(gè)對(duì)象。
同理可得:
?
filter(list, _ > 0)
?能得到一個(gè)只有原列表中正數(shù)的新列表。?還有高級(jí)版的排序:
sort_key(list, -_)
?實(shí)現(xiàn)了逆序排序。
之前提到了流操作,確實(shí)很像,然而并不一樣:根本沒有那種優(yōu)化! 例如:first(sort(list), true)
?的性能比效果上等價(jià)的?min(list)
?要差得多!
另外,列表只能在最后添加,不能中間插入,不能中間刪除,但是可以根據(jù)下標(biāo)截取出一個(gè)新的列表——其他的功能要自己寫! 舉個(gè)例子:
remove_by_val(list, item) -> (
? ?
? new_list = l();
? for (list,
? ? if (item == _,
? ? ? ?put(new_list, null, _)
?? ?)
??);
??return(new_list)
);
沒錯(cuò),for
?和?if
?也是函數(shù),只不過能傳入語(yǔ)句。具體要看官方手冊(cè)的定義,同樣的還有?loop
,while
。另外這里沒有方便的?break
?和?continue
,可能需要立flag或者用超大的條件判斷。
看到這里,你可能會(huì)覺得不妙:那咋取出列表的特定元素?答案是?element(<list>, <index>)
,element有點(diǎn)奇妙的特性,上文說過。
那關(guān)于Minecraft的功能呢???
抱歉,我沒怎么了解(笑)
不過看手冊(cè),功能還是挺全的,可以控制時(shí)間流逝速度,進(jìn)行隨機(jī)刻,搜索實(shí)體,獲取 NBT 標(biāo)簽,生成結(jié)構(gòu),執(zhí)行命令,甚至方便地檢測(cè)一片區(qū)域里的方塊……畢竟作者在技術(shù)社區(qū)混了那么久,基本上你想到的需求是一應(yīng)俱全的。(實(shí)時(shí)生物AI追蹤呢?)
不過也要小心一點(diǎn)。比起命令方塊,Carpet 的安全性要差一點(diǎn)。例如一次在聊天欄輸出超大的列表會(huì)卡死客戶端,服務(wù)端在執(zhí)行腳本的時(shí)候默認(rèn)會(huì)凍結(jié)住之類的……