May 12,2008
Boo(11)-Hash
Hash 的用法很簡單,同樣地,跟 array、List 一樣,可以用很簡潔的方式來表示,也就是大括號 { }
或者,也可以將符合 key、value 格式的有 IEnumerable 介面的變數傳入 Hash() 函數來取得。
沒有 Generic 版本的 Hash 可以參考 src/boo.lang/Hash.cs,它其實是繼承自 Hashtable﹔有 Generic 版本的,就直接參考 System.Collections.Generic 裡面的 Hash 吧~
或者,也可以將符合 key、value 格式的有 IEnumerable 介面的變數傳入 Hash() 函數來取得。
h1 = { 'a': 65, 'b': 66, 'c': 67 }
print h1['a']
h2 = Hash( ( ('a',65), ('b',66), ('c',67) ) )
h3 = Hash( [ ('a',65), ('b',66), ('c',67) ] )
沒有 Generic 版本的 Hash 可以參考 src/boo.lang/Hash.cs,它其實是繼承自 Hashtable﹔有 Generic 版本的,就直接參考 System.Collections.Generic 裡面的 Hash 吧~
May 8,2008
Boo(10)-Array
陣列的定義方法主要有兩種:
- 使用小括號 (,) 來定義。
- 使用函數: array()、matrix() 來取得。
a0 = (,) // 空的陣列 a1 = ( 1, 2, 3, 4, 5 ) // 都是整數的陣列 a2 = array( range(5) ) // 同樣也是得到整數陣列 a3 = matrix( typeof(int), 2, 3 ) // 得到一個 2x3 的陣列,也可以多傳幾個,製造一個瘋狂的多維陣列 a4 = array( typeof(int), 5 ) // 也是得到一個整數陣列 a3[0,0] = 1 a3[1,0] = 2 print "len(a3,0)=" + len(a3,0) // 得到 a3 第一維的大小 print "len(a3,1)=" + len(a3,1) // 得到 a3 第二維的大小 for i in a3: print i a4 = ( 1, 'a', 2, 'b' ) // 雖然陣列要求都是相同型別,但是這樣寫也可以,只是得到型別都是 object 的陣列 // 輸出結果 // 1 // 0 // 0 // 2 // 0 // 0 // len(a3,0)=2 // len(a3,1)=3陣列也可以用 Generic 語法(參考原始碼 tests/testcases/parser/array_list_hash_literals.boo),只是我覺得並不是像 List 那麼的必要就是了~:
a5=(of int: 1,2,3)
偵測 .Net/Mono 安裝目錄與 CLR 版本
參考自:How to determine the .NET installation directory and CLR version
env=System.Runtime.InteropServices.RuntimeEnvironment() print env.GetRuntimeDirectory() print env.GetSystemVersion() print env.SystemConfigurationFile // 輸出結果 (Ubuntu 8.04 + Mono 1.2.6) // /usr/lib/mono/2.0 // v2.0.50727 // /etc/mono/2.0/machine.config
May 6,2008
Boo(9)-List
Boo的 List 並不是使用 .Net/Mono 的 List,而是自己實作。
使用的方法很簡單,用中括號或是使用List函數。
如果你想知道更多 List 的操作,可以在 booish 裡面使用 dir(),來查看。
由於 dir() 會傳回一個 IEnumerator,所以你也可以用下面的程式把每個項目輸出。
或者,直接參考 Boo 的原始碼 src/boo.lang/list.cs 。
隨著 .Net framework 2.0 推出,Boo 也支援了 Generic 語法,語法可以參考這篇的說明:Boo Generic Syntax
首要的一件事,就是先 import System.Collections.Generic,表明要使用 Generic,否則之後的程式會無法執行。接著使用 [of Type] 的語法,表示 List 要使用指定 Type 的泛型。
使用泛型最明顯的好處是省去轉型的麻煩,因為 List 裡面預設都是使用 object 型別,改用 Generic 以後,可以預先指定好 List 裡面的元素要使用什麼型別。
使用的方法很簡單,用中括號或是使用List函數。
l1=[ 1, 2, 3, 4, "a", "b", "c" ] l2=List( range(10) ) l1.Add( "d" ) l2.Add( "d" ) print l1 print l2 l1.Remove( "d" ) // 輸出結果 // [1, 2, 3, 4, "a", "b", "c", "d"] // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "d"] // [1, 2, 3, 4, "a", "b", "c"]除了正常 List 的操作 Add()、Remove() 以外,還提供了 Push()、Pop(),這意味著可以把 List 當 Stack 來使用。
如果你想知道更多 List 的操作,可以在 booish 裡面使用 dir(),來查看。
>>>l=[] >>>dir(l)
由於 dir() 會傳回一個 IEnumerator,所以你也可以用下面的程式把每個項目輸出。
l=[] for m in dir(l): print m
或者,直接參考 Boo 的原始碼 src/boo.lang/list.cs 。
隨著 .Net framework 2.0 推出,Boo 也支援了 Generic 語法,語法可以參考這篇的說明:Boo Generic Syntax
import System.Collections.Generic l = List[of int]() l.Add( 10 ) l.Add( 20 ) for i in l: print i l.Add( "hello" ) // 這行將會發生錯誤,告訴你不可以加入字串型別
首要的一件事,就是先 import System.Collections.Generic,表明要使用 Generic,否則之後的程式會無法執行。接著使用 [of Type] 的語法,表示 List 要使用指定 Type 的泛型。
使用泛型最明顯的好處是省去轉型的麻煩,因為 List 裡面預設都是使用 object 型別,改用 Generic 以後,可以預先指定好 List 裡面的元素要使用什麼型別。
May 2,2008
求1到50之間所有偶數的平方值總和
剛好看到:求1到50之間所有偶數的平方值總和,於是順手寫一寫:
// // Sum all even in 1~50 // import System sum = 0 for i in range(1,51): if i%2==0: sum+=i*i print sum
April 30,2008
Boo(8)-迴圈
基本上迴圈有兩種:for 與 while。
for 與一般語言的 for 不太一樣,反而與 foreach 比較類似,為了要能得到一個 Enumerator,通常都搭配 range():
for 與一般語言的 for 不太一樣,反而與 foreach 比較類似,為了要能得到一個 Enumerator,通常都搭配 range():
// 印出 0 到 4 for i in range(5): print iwhile 也沒什麼特別的:
// 同樣印出 0 到 4 i=0 while i<4: print i i=i+1Boo 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
剛好遇到這種狀況,要依據字串的某字元然後做出陣列。所以很直覺地,就可以寫出這樣的代碼。
非常感謝Google 網上論壇 的Boo Programming Language群組: 正好解答了我的問題,原來要這樣寫:
這真是太隱晦不明了...
splitter = ( char(','), char('\n') )
fields = inputText.Split( splitter )
不過這段代碼足以讓人搞半天了,boo 會不讓你執行。非常感謝Google 網上論壇 的Boo Programming Language群組: 正好解答了我的問題,原來要這樣寫:
splitter = ( char(','), char('\n') )
fields = inputText.Split( *splitter )
這真是太隱晦不明了...
April 28,2008
Boo(7) - if-elif-else、unless
Boo 的 if 述句與 python 相似:
很簡單。這邊額外要提到程式區塊的概念,像 C# 是用 { },Pascal 用 begin end,Python 與 Boo 是用縮排來決定程式區塊,所以縮排使用的字元很重要,千萬不要混雜使用,否則你會錯的莫名其妙,在開發時,最好一開始就決定好要使用 tab 字元或是特定數目的空白字元。
運算式,可以使用 and, or 來作連接,或是使用 not 來表示需要相反的條件式。
如果你沒接觸過 perl/python/ruby/php 的話,以下的用法應該會讓你感到新奇:
unless 是反面的 if,把它想成 if not 就對了,所以第三行並不會被執行。
此外,unless 與 if 也可以用來修飾前面的述句﹔以第四行來說,unless s=="sad"用來修飾前面的 print "Hello world!"﹔以第五行來說,if s=="sad"用來修飾前面的print "I am sad"﹔當條件符合時,才會印出。
因此第四行的 "Hello world!" 不會被印出,而第五行的 "I am sad" 則會被印出。
這些用法會讓人覺得程式也很口語化~
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" 則會被印出。
這些用法會讓人覺得程式也很口語化~
April 23,2008
Boo(6)-變數
Boo 的變數宣告方法很簡單,就跟大多數的 script 語言一樣,指定即用。
比較特別的地方有三個:
經過編譯(booc -target:exe var.boo)以後,再用 reflector 去反組譯,產出的C#代碼如下:
從反組譯出來的結果看來,Boo都幫你自動處理好了,的確是不用特別花心思去寫。
比較特別的地方有三個:
- 在第一次指定以後,該變數型別就確定了,之後若指定其他型別的值給它,會發生錯誤。這邊的行為跟 Python、Ruby、Javascript等 script 語言不同,要特別注意。這也證明了 Boo 具有靜態語言的特性。
- 字串裡可以用 ${var_name} 的方式,會直接把 var_name 的值放到變數裡面去。
- 字串有三種定義方法:
- 雙引號,如"Hello"
- 單引號:跟雙引號的不同處,在於單引號字串不解釋 ${},所以 print '${var}' 會印出 ${var},而不是 var 變數的值。
- 三個雙引號,如 """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都幫你自動處理好了,的確是不用特別花心思去寫。
April 21,2008
Boo(5) - Console.ReadKey()
承接上篇的討論,經過 Hack 之後,發現原因就出在 Console.ReadKey()。
booish 與 IronPython 為了要能達到自己的需求,所以並不使用 Console.ReadLine(),而是使用 Console.ReadKey() 自行處理收到的按鍵並輸出。
為了要知道為甚麼會有問題,我試著寫了一個小程式,想知道相似的方式,是否也會有同樣的問題發生:
經過用Reflector比對以後,發現Mono的 Console.ReadKey() 的迴圈條件式有問題。
如果將該條件式修正為:
總之,一切就此水落石出。
等等,可是為甚麼 Console.ReadLine() 沒有問題呢?嗯嗯,這邊的原因也很簡單,因為不管是 .NET 的 CLR 或是 Mono,都是調用 stdin 進行處理,所以就沒有這樣的問題了。
解決方法,目前並沒有,應急的方法是自行下載原始碼,修正Console.ReadKey()(在class/corlib/System/WindowsConsoleDriver.cs)以後,再編譯。不急的話,是先上Mono Buzilla去找找看是否有人回報過此問題,如果沒有再回報上去。
晚點要去找找看,然後回報上去,希望下個版本能解決。
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=='\0' 與 VirtualKeyCode 不是 Ctrl、alt、shift、numlock、caplock、scrolllock 的情況 //} while( !(record.EventType==1 && record.KeyDown ) || record.Character=='\0' && ( ((record.VirtualKeyCode<0x10 || record.VirtualKeyCode>0x12) && 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去找找看是否有人回報過此問題,如果沒有再回報上去。
晚點要去找找看,然後回報上去,希望下個版本能解決。