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等支援,請你自己看看吧。

No comments:

Post a Comment