Boo(8)-迴圈

基本上迴圈有兩種:for 與 while。
for 與一般語言的 for 不太一樣,反而與 foreach 比較類似,為了要能得到一個 Enumerator,通常都搭配 range():

// 印出 0 到 4
for i in range(5):
print i

while 也沒什麼特別的:

// 同樣印出 0 到 4
i=0
while i<4:
print i
i=i+1

Boo Primer還有提出所謂的 do-while,但實際上是運用 while + break + unless修飾詞來達成的:

// 也是印出 0 到 4
i=0
while true:
print i
i=i+1
break unless i<4

有 break,當然也有 continue:

// 印出 1 3 5 7 9
for i in range(10):
continue if i%2==0
print i

另外再提一個關鍵字,就是 pass,這用來表示程式區塊內不做事情:

// 不會輸出任何結果,因為被 pass 掉了...
i=2
if i%2==0:
pass
else:
print "i!=2"

Boo and Split

剛好遇到這種狀況,要依據字串的某字元然後做出陣列。所以很直覺地,就可以寫出這樣的代碼。

splitter = ( char(','), char('\n') )
fields = inputText.Split( splitter )

不過這段代碼足以讓人搞半天了,boo 會不讓你執行。
非常感謝Google 網上論壇 的Boo Programming Language群組

正好解答了我的問題,原來要這樣寫:

splitter = ( char(','), char('\n') )
fields = inputText.Split( *splitter )

這真是太隱晦不明了…

Boo(7) – if-elif-else、unless

Boo 的 if 述句與 python 相似:

i=5
if i>5:
print "i大於5"
elif i==5:
print "i等於5"
else:
print "i小於5"

很簡單。這邊額外要提到程式區塊的概念,像 C# 是用 { },Pascal 用 begin end,Python 與 Boo 是用縮排來決定程式區塊,所以縮排使用的字元很重要,千萬不要混雜使用,否則你會錯的莫名其妙,在開發時,最好一開始就決定好要使用 tab 字元或是特定數目的空白字元。

運算式,可以使用 and, or 來作連接,或是使用 not 來表示需要相反的條件式。

i=7
if i>=5 and i<=10:
print "i 介於 5~10 之間"
else:
print "超出範圍"
if not i==5:
print "i不等於5"

如果你沒接觸過 perl/python/ruby/php 的話,以下的用法應該會讓你感到新奇:

s="sad"
unless s=="sad":
print "Hello world!"
print "Hello world!" unless s=="sad"
print "I am sad" if s=="sad"

unless 是反面的 if,把它想成 if not 就對了,所以第三行並不會被執行。
此外,unless 與 if 也可以用來修飾前面的述句﹔以第四行來說,unless s==”sad”用來修飾前面的 print “Hello world!”﹔以第五行來說,if s==”sad”用來修飾前面的print “I am sad”﹔當條件符合時,才會印出。
因此第四行的 “Hello world!” 不會被印出,而第五行的 “I am sad” 則會被印出。

這些用法會讓人覺得程式也很口語化~

電影流水帳(2008/04/25~2008/04/26)

兩天衝完四部片,夠猛!不過只有一部片我覺得比較好看~

  • Step up(Wikipedia),中譯:舞出我人生。這應該算是青春勵志片,一個小混混因為跟朋友進馬里蘭學校搞破壞,結果被逮住,法庭判他去學校勞動服務。事情往往就是那麼剛好,女主角因為舞伴受傷,剛好又發現他有才華,於是就找他幫忙,就這樣,發生了一連串的故事。普通,今年好像要上 2 的樣子。
  • Miss little sunshine(IMDB, Wikipedia),中譯:小太陽的願望。這部片子蠻好看的耶,帶小女兒去參加選美的旅行途中發生了一連串的事情,最後大家都解開心結,真的相當好看。
  • Catch that kid(Wikipedia),中譯:小鬼神偷。深愛老爸的女主角,為了突然癱瘓的老爸鋌而走險,去偷銀行的錢,最後以喜劇收場。偷銀行,這算是拍給小朋友看的電影嗎?整體來說普普通通,女主角後來有演出Jumper(移動世界),擔任女主角年輕的時候,出現片段很短。
  • Miami Vice(IMDB, Wikipedia),中譯:邁阿密風雲。雖說是從影集翻拍的,但老實說,我覺得翻拍的不怎麼樣,劇情普通。原本對這部片子很期待的說,因為演員陣容還蠻堅強的。

電影流水帳(2008/04/22~2008/04/24)

真的是太拼了,短短三天看完兩部片子!

  • Vantage Point(IMDB, Wikipedia),中譯:刺殺據點。實際上整個事件發生的時間我想只有大概二十到三十分鐘,或許更短也不一定,短短的時間內,可以從多個面向去把事件發生的經過重現一遍,然後剪成一小時約 30 分鐘的片子。我覺得很不簡單,片子看起來一氣呵成,不知道導演出動了多少攝影機,才能一氣呵成地把所有發生的經過拍下來?還不錯看的片子。
  • 奪帥(IMDB, Wikipedia),另外一個名字:血戰。演員陣容堅強,不過可惜,故事不怎麼樣,有點不知所云的感覺。唉~我的時間浪費掉了啊~

Boo(6)-變數

Boo 的變數宣告方法很簡單,就跟大多數的 script 語言一樣,指定即用。
比較特別的地方有三個:

  • 在第一次指定以後,該變數型別就確定了,之後若指定其他型別的值給它,會發生錯誤。這邊的行為跟 Python、Ruby、Javascript等 script 語言不同,要特別注意。這也證明了 Boo 具有靜態語言的特性。
  • 字串裡可以用 ${var_name} 的方式,會直接把 var_name 的值放到變數裡面去。
  • 字串有三種定義方法:
    1. 雙引號,如”Hello”
    2. 單引號:跟雙引號的不同處,在於單引號字串不解釋 ${},所以 print ‘${var}’ 會印出 ${var},而不是 var 變數的值。
    3. 三個雙引號,如 “””Hello”””,跟前兩者的差別在於可以跨越多行。這跟 python 一樣。
// var.boo
i = 100
s = "Hello world ${i} times!!"
print s
// 如果把 100 指派給 s,就會發生錯誤
// s = 100
// Boo Primer 說可以像下面這樣寫,但經過我試驗,是不行的。
// s as int = 200
// 但這樣寫,卻是可以的
o as object = 300
print o
o = "I am object"
print o
print """Hello
Line1
Line2
Line3"""

Boo 官方建議不要特別花心思去寫 <variable> as <type> = <value> 這樣的語法,盡量使用 <variable> = <value>。
經過編譯(booc -target:exe var.boo)以後,再用 reflector 去反組譯,產出的C#代碼如下:

private static void Main(string[] argv)
{
int num = 100;
Console.WriteLine(new StringBuilder("Hello world ").Append(num).Append(" times!!").ToString());
object obj2 = 300;
Console.WriteLine(obj2);
obj2 = "I am object";
Console.WriteLine(obj2);
Console.WriteLine("Hello\r\nLine1\r\nLine2\r\nLine3");
}

從反組譯出來的結果看來,Boo都幫你自動處理好了,的確是不用特別花心思去寫。

電影流水帳(2008/04/17~2008/04/21)

一部是可能有續集的動作片,另一部算是劇情片。有時候在想,我是不是應該要跟一些專門介紹電影的網站一樣,寫的長篇大論呢?還是保持我言簡意賅的風格,簡單交代我的感想即可?

  • Jumper(IMDB, Wikipedia),中譯:移動世界,中文片名我倒是覺得可以用時空跳躍者。這故事不錯,看了維基百科上的介紹,果然是由Steven Gould的小說改編,劇情介紹就不多說了,網路上還蠻多人推薦這部片子的,Google it!整部片子到最後,仍然是沒有解釋為什麼Paladin要追蹤並殺害Jumper,我想這應該會有第二集來作Paladin與Jumper的前因後果吧~
  • The bucket list(IMDB, Wikipedia),中譯:一路玩到掛。雖然中譯片名與英文片名不符,但是我覺得意思有到了,片子的確就是告訴你,不要愁眉慘霧地等死,勇敢地把想做的事情列出來,然後努力去作,這不就是一路玩到掛?不過其實有很多事情並不是真的說要作就能做的,還是得像片裡的Jack Nicholson一樣那麼有錢才行啊~附帶一提,找這兩個老傢伙演員演,真的是找對人了。

Boo(5) – Console.ReadKey()

承接上篇的討論,經過 Hack 之後,發現原因就出在 Console.ReadKey()。

booish 與 IronPython 為了要能達到自己的需求,所以並不使用 Console.ReadLine(),而是使用 Console.ReadKey() 自行處理收到的按鍵並輸出。
為了要知道為甚麼會有問題,我試著寫了一個小程式,想知道相似的方式,是否也會有同樣的問題發生:

// Program: readkey.cs
// How to compile: gmcs /target:exe readkey.cs
using System;
public class Hello {
public static void Main( string[] args ) {
Console.TreatControlCAsInput = true;
ConsoleKeyInfo keyInfo;
do
{
keyInfo = Console.ReadKey( true );
Console.Write( keyInfo.KeyChar );
} while( keyInfo.Key!=ConsoleKey.Escape );
}
}

這個小程式編譯以後,使用 .NET 來執行,一切正常,但使用Mono Windows 版來執行,就硬是會 echo 出兩次。
經過用Reflector比對以後,發現Mono的 Console.ReadKey() 的迴圈條件式有問題。
如果將該條件式修正為:

// } while( record.EventType != 1 && !record.KeyDown); // original
} while( !(record.EventType==1 && record.KeyDown ) );
// 附帶一提,這是 Windows .NET 的版本的變形,多判斷了 Character=='' 與 VirtualKeyCode 不是 Ctrl、alt、shift、numlock、caplock、scrolllock 的情況
//} while( !(record.EventType==1 && record.KeyDown ) || record.Character=='' && ( ((record.VirtualKeyCode0x12) && record.VirtualKeyCode!=0x14 && record.VirtualKeyCode!=0x90 ) ? (record.VirtualKeyCode==0x91) : true ) );

就可以解決問題,我想這可能是因為寫作 WindowsConsoleDriver.cs 的人對 ReadConsoleInput() 不夠清楚的緣故,不過其實我也是看了 source code 才知道要這樣去判斷。
總之,一切就此水落石出。

等等,可是為甚麼 Console.ReadLine() 沒有問題呢?嗯嗯,這邊的原因也很簡單,因為不管是 .NET 的 CLR 或是 Mono,都是調用 stdin 進行處理,所以就沒有這樣的問題了。

解決方法,目前並沒有,應急的方法是自行下載原始碼,修正Console.ReadKey()(在class/corlib/System/WindowsConsoleDriver.cs)以後,再編譯。不急的話,是先上Mono Buzilla去找找看是否有人回報過此問題,如果沒有再回報上去。
晚點要去找找看,然後回報上去,希望下個版本能解決。

Boo(4) – booi 與 booish

Boo 有兩個很方便的工具:booi 與 booish
booi 可以用來直接執行你寫好的 script,而不必事先編譯。
而 booish 則可以讓你直接輸入指令,立即就能看到結果。
類比其他語言的話,booi 用法就像是執行 wscript your_script 或是 python your_script:booi your_script。
booish 就像是單獨執行 Pythonruby 是一樣的,執行以後,會出現訊息以及提示符號。
booish 的提示符號是 >>> ,與 Python 一樣,使用方法也很類似。
使用 booish 的時候,我發現很詭異的地方,就是使用 Mono 包進來的 booish 與 ipy (IronPython) 時,輸入任何字元,都會自動出現兩次以上…
boo 官方網站所提供的 booish 就沒有這個問題,這讓我很納悶。
有時間來找找這問題發生的原因。