2011/08/23

MoonScript幾支小程式

這篇要寫幾支MoonScript程式。

關於MoonScript的概觀介紹請看這篇如何安裝看這篇,完整的MoonScript語言參考手冊在這裡(英文)。

注意:我假設你已經會Lua了。

首先,向世界說聲哈囉吧。

寫一支沒有參數的函式並呼叫它。

func_hello = -> print "hello world"
func_hello!
func_hello()

輸出:
hello world
hello world

用->(箭頭)來產生函式,用=(等號)把產生出來的函式指派給func_hello,無參數的函式,除了可以用一般的()括號來呼叫,也可以用!(驚嘆號)來呼叫。而且這支函式的內容只有一個述句(statement),所以直接寫在->的後面即可。

如果函式內容多於一個述句,要縮排後一行一行放在->之下。

func_hi = ->
  name = "John Smith"
  print "Hi, I am " .. name

接下來,有參數的函式,要把參數宣告在->之前,以,(逗號)分開,以()括號包起來。

func_add = (x, y) -> return x + y
func_add2 = (x, y) -> x + y
print(func_add(1, 2))
print(func_add2(3, 4))

輸出:
3
7

func_add有return,func_add2沒有,但兩者是一樣的,這叫做implicit return,函式執行時最後的運算式,會回傳給呼叫者。

函式可以回傳多個值。

power_of_2_3 = (x) -> x*x, x*x*x
a, b = power_of_2_3 5
print a
print b

輸出:
25
125

函式呼叫可省略掉括號。

print(func_add(7, 8))
print func_add 7, 8

輸出:
15
15

省略括號後,參數會被傳進離它最近的函式,所以上面的7跟8會被傳進func_add。

當情況比較複雜會搞混時,當你想控制誰是誰的參數時,就用()括號來區分。

print "9 + 10 =", func_add(9, 10), "11 + 12 =", func_add(11, 12)


可以讓參數有預設值。

func_inc = (a, b=1) -> a + b
print func_inc 10, 5
print func_inc 10

輸出:
15
11

參數預設值的運算式,會一個跟著一個被執行,所以,後面參數可以使用前面參數。

foo = (x=100, y=x+5) -> x + y
print foo!
print foo 95

輸出:
205
195

接下來,介紹非常好用的table comprehension。過去,第一次看到Python的list comprehension時, 就覺得這玩意兒怎麼這麼強啊,真是太好用了,程式碼會變得更簡短,光一行就能完成超多事情。comprehension就好像把資料結構、迴圈、條件判斷 式通通攪和在一起,是個高階的程式概念,底下,就讓我用MoonScript來練習寫幾支table comprehension的程式吧。

若scores裡面是學生的成績,考的太差了,只好開根號乘以10,調整一下成績,以免當太多人,下次開課會教室不夠大。

scores = {64, 33, 25, 38, 48}

scores_modified = [math.sqrt(s) * 10 for i, s in ipairs scores]

print s - s%1 for i, s in ipairs scores_modified

首先,宣告變數scores,用{}大括號產生出table,裡面放了五個人的分數,在這裡,我們把table當做array或list來用,也就是說,裡面的項目是逐一排列的。然後,我們要把這個table裡面的項目,一個一個拿出來,也就是for i, s in ipairs scores這段所做的事情,i會是index,從1到5,在這裡沒有用到,s會是那五個成績,拿到成績後,開根號乘以10,算出調整後的成績,然後用[]方括號包起來,這就是comprehension,結果會是一個table,指派給scores_modified。

然後,用迴圈輸出成績,其中s - s%1是取出整數的部分,小數我們就不要了。

讓我們算一算調整前後各有幾個人被當。

stemp = [s for s in *scores_modified when s < 60]

print #stemp

同樣使用comprehension,因為逐一取出項目這個動作很常見,所以MoonScript加入一個語法,請看for s in *scores_modified,這個*星號,會等於for i, s in ipairs scores_modified,還有,我們用when來篩選出想要的項目,在這裡是小於60的成績,也就是不及格的,然後把結果放進table stemp裡。

#井號接table,表示table的大小,不過,這只有在我們把table當做array來用時,這個運算子#才會得到正確的結果。

執行看看吧,調整成績後,還有多少人會被當呢?

接下來,MoonScript與Lua有個不一樣的地方,就是if與for不僅是statement,也是expression,意思是說,它們都能回傳值。先看看if。

寫一個函式,給定一個成績參數,判斷是否過關或被當,第二個參數可有可無,若有,表示過關的最低成績,預設值是60。

pass_or_fail = (score, score_to_pass = 60) -> score >= score_to_pass

result = ""
if pass_or_fail 60
  result = "Pass"
else
  result = "Fail"

result = if pass_or_fail 60
  "Pass"
else
  "Fail"

如果if只是statement,不是expression的話,我們就必須要用上半部那種寫法;但MoonScript是expression,所以就能用下半部那種寫法。這兩種寫法的結果result會是相同的。

接下來看看for可以怎麼用。假設從1到10,奇數就保留,偶數就乘以2,怎麼寫呢?

doubled_evens = {}
for i=1, 10
  if i % 2 == 0
    doubled_evens[i] = i * 2
  else
    doubled_evens[i] = i

doubled_evens = for i=1, 10
  if i % 2 == 0
    i * 2
  else
    i

如果for只是statement,那就要用上半部的寫法,但MoonScript的for也是expression,所以可以用下半部的寫法,for每次迴圈的值,會被收集起來,放進table裡,最後被指配給doubled_evens。所以,這兩種寫法的結果相同。

另外,雖然Lua也可以寫OOP,但沒有直接的語法支援,總是會有礙手礙腳的地方,MoonScript加入了class、inheritance等支援,請你自己看看吧。

2011/08/22

MoonScript概觀介紹

這一篇把MoonScript的首頁,概略翻譯出來。

MoonScript是個動態腳本語言,以CoffeeScript的語法為基礎,不過,編譯後的語言是Lua

成功安裝MoonScript後,你會得到:兩支執行檔moonmoonc,以及Lua模組moonscript。使用moon可以直接執行一支MoonScript程式檔;使用moonc可以把MoonScript程式檔編譯轉成Lua程式檔,之後再執行;在Lua程式裡加上require "moonscript",就能看懂、載入、執行MoonScript檔案了。

因為編譯後的結果是Lua程式碼,所以相容於各種Lua實作,包括LuaJIT,也相容於所有已經寫好的的Lua程式庫。

完整語言的介紹,請見MoonScript語言參考手冊(英文),我也寫了幾支小小的程式。接下來要概略介紹一下,因為我們可把MoonScript想像成為Lua披上並擴充較好看的語法外衣,所以你需要對Lua具有一定程度的了解。


概觀

MoonScript提供簡潔的語法,利用縮排來判斷並分割程式碼的各部分,而不是像Lua一樣使用囉嗦的關鍵字,也不是像C語言一樣使用大括號,利用縮排來定義語法的程式語言,有名的有Python。底下是MoonScript的一些述句構成。

export my_func
x = 2323

collection =
   height: 32434
   hats: {"tophat", "bball", "bowler"}

my_func = (a) -> x + a

print my_func 100

其了較簡潔的語法外,還加入了其他特色,包括table comprehensions、函式裡的implicit return、類別(class)、繼承(inheritance)、scope的管理述句import與export、以及很便利的物件建構述句with。

import concat, insert from table

double_args = (...) ->
   [x * 2 for x in *{...}]

tuples = [{k, v} for k,v in ipairs my_table]

當有錯誤發生時,它還能指出是在原先檔案裡的哪一行出錯,而不僅是編譯後的檔案。


安裝

最容易的方式是利用LuaRocks,以底下提供的rockspec來安裝:
> luarocks build http://moonscript.org/rocks/moonscript-0.1.0-1.rockspec

詳細安裝過程,我寫在另外一篇

選用功能

如果你使用Linux,並且想用watch模式,此模式會監視.moon檔,當有變動時就自動編譯成.lua檔。你需要安裝linotify


原始碼

專案原始碼放在GitHub上:https://github.com/leafo/moonscript

有任何問題,請到這裡回報:https://github.com/leafo/moonscript/issues

最新的開發版本(或許不能動喔)可以用底下的rockspec安裝:
> luarocks build http://moonscript.org/rocks/moonscript-dev-1.rockspec


相依於其他軟體套件

除了Lua 5.1外,MoonScript還需要底下的Lua模組:
  • linotify(在Linux上的選用功能)

若你使用LuaRocks來安裝MoonScript,這些套件都會被自動取回並安裝。


學習

完整的語言參考手冊(英文)在此。


其他外掛

差異處的概略介紹
  • 利用縮排與空白字元來定義出程式區塊
  • 所有變數宣告,預設為區域變數。
  • export關鍵字來宣告全域變數,用import關鍵字來匯入table裡的東西,也就是取得一份區域性的拷貝。
  • 函式呼叫時,括號是可有可無的,類似於Ruby。
  • 胖箭頭,=>,用來產生具有self參數的函式。
  • 在名稱之前加上@(小老鼠),用來指稱它是個self裡的東西。
  • 運算子!(驚嘆號),可用來呼叫無參數的函式。
  • 根據函式裡最後一個述句的型別,自動加上implicit return。
  • 使用:(冒號)來分開table裡的鍵與值,而不是用=(等號)。
  • 換行(newline)可用來區分開table裡的每一項目,逗號(,)也可以。
  • \(反斜線)來呼叫物件的方法,而不是用:(冒號)。
  • 支援+=-=/=*=%=運算子。
  • !=~=的別名。
  • table comprehension,很便利的slicing與iterator語法。
  • 程式碼若一行,可以在後面加上迴圈與if述句。
  • if述句,可當做運算式使用。
  • 具有繼承的類別系統,建構在metatable __index屬性之上。
  • 建構子的參數,若以@開頭,會自動指定給物件。
  • 魔法般的super函式,將同名的類別方法對應到父類別的方法。
  • with述句,讓你以較短的語法存取無名的物件。


關於

MoonScript的語法,有很大程度是被CoffeeScript激發而來的。

沒有LPeg這個超棒超強的語法解析工具,MoonScript是不可能誕生的。

2011/08/21

MoonScript安裝

這一篇要講如何安裝MoonScript,至於概觀介紹可看這一篇

我先試著在Windows XP與Cygwin上安裝,但都沒有成功,MoonScript作者也說他主要的開發平台是Linux,所以我才在我的小白Mac OS X 10.6.8上安裝,滿順利的,後來又在Windows XP裡的VirtualBox裝Ubuntu 11.04,也滿順利的。


概略步驟如下:
1. 安裝Lua
2. 安裝LuaRocks
3. 安裝MoonScript(其中某部分需要安裝git)


在Windows上的安裝過程:
我沒有成功,你可以參考這一篇,看看眾多複雜的過程,以及最後出現的問題。


在Ubuntu上的安裝過程:
因為軟體都以套件打包好了,所以安裝很順利。

1. 安裝Lua
sudo apt-get install lua5.1

2. 安裝LuaRocks
sudo apt-get install luarocks

3. 安裝MoonScript(其中某部分需要安裝git)
sudo apt-get install git(若你沒有安裝過git的話)
然後以LuaRocks安裝MoonScript(底下這行指令是MoonScript官方網站寫的):
luarocks build http://moonscript.org/rocks/moonscript-0.1.0-1.rockspec

MoonScript需要LPeg、LuaFileSystem、alt-getopt這三個套件,所以會先安裝它們。

安裝成功後,moon跟moonc這兩個執行檔會在~/.luarocks/bin/下。


在Mac OS X上的安裝過程:
1. 安裝Lua
官方網站的下載區下載原始碼,我下載安裝的版本是lua-5.1.4.tar.gz。
tar zxvf lua-5.1.4.tar.gz,解壓縮。
cd lua-5.1.4,進解壓縮後的目錄裡。
make macosx,建構。
make install (sudo make install),安裝。

預設值會安裝到/usr/local下,執行檔(lua與luac)在bin底下,其他檔案散佈在include、lib、man、share底下。

2. 安裝LuaRocks
官方網站下載,我下載的是luarocks-2.0.5.tar.gz,

tar zxvf luarocks-2.0.5.tar.gz,解壓縮。
cd luarocks-2.0.5,進解壓縮後的目錄裡。
./configure
make
sudo make install

預設值會安裝到/usr/local下,執行檔(luarocks與luarocks-admin)在bin底下。


3. 安裝MoonScript(其中某部分需要安裝git)
這裡抓取Mac OS X的git安裝程式,按照下載後的dmg檔裡面的README.txt的指示安裝。

然後以LuaRocks安裝MoonScript(底下這行指令是MoonScript官方網站寫的):
luarocks build http://moonscript.org/rocks/moonscript-0.1.0-1.rockspec

MoonScript相依於LPeg、LuaFileSystem、alt-getopt這三個套件,所以會先安裝它們。


安裝成功的話,最後應該會出現如下的訊息:
moonscript 0.1.0-1 is now built and installed in /usr/local/ (license: MIT)

moon a.moon to run moonscript script file
moonc a.moon to compile it to lua code

moon跟moonc這兩個執行檔會在/usr/local/bin/下。


接下來就是寫MoonScript的程式,附檔名用.moon,以"moon xyz.moon"來直接執行,或是以"moonc xyz.moon"把MoonScript程式轉成Lua程式。請確定moon與moonc這兩支執行檔有在你的執行路徑PATH下。

完整的語言參考手冊在這裡(英文)。