BOO入門/容器與轉型
BOO入門 > 容器與轉型 (上一章:流程控制-迴圈 下一章:運算子)
串列
[编辑]串列可以很容易地增/刪裡面的元素。
// lists
print([0, 'alpha', 4.5, char('d')])
print List('abcdefghij')
l = List(range(5))
print l
l[2] = 5
print l
l[3] = 'banana'
print l
l.Add(100.1)
print l
l.Remove(1)
print l
for item in l:
print item
輸出結果
[0, alpha, 4.5, d] [a, b, c, d, e, f, g, h, i, j] [0, 1, 2, 3, 4] [0, 1, 5, 3, 4] [0, 1, 5, 'banana', 4] [0, 1, 5, 'banana', 4, 100.1] [0, 5, 'banana', 4, 100.1] 0 5 'banana' 4 100.1
從例子可以看到,串列很有彈性,也很方便。 定義的方法有兩種:
- 使用中括號 []
- 將列舉子(IEnumator)或陣列傳入 List 函數中。
譯註
[编辑]- 例子的第4行 List( range(5) ) 就是將列舉子(IEnumerator)傳入,range() 傳回的是一個 IEnumerator﹔ List( ( 1,2,3) ) 則是將陣列傳入,關於陣列的部份,下節會提到。
- 串列無法事先指定所要使用的型別,裡面的每個元素都是 object。因此轉換成陣列時,你需要確定每個元素都是你要的型別才能轉換,否則 Boo 會告知錯誤。
- 由於串列提供了 Push()、Pop() 函數,所以你可以把它當 Stack 來使用。
- 串列可以使用 Collect() 將符合條件的元素提取出來:
// Collect sample
def IsString( o as object ):
s = ' '
if o.GetType()==s.GetType():
return true
else:
return false
l = [ '1','2','3', 1, 2, 3 ]
m = l.Collect( IsString )
print m
List 除了這些以外,還有一些方法可以操作,如:*、AddUnique、Add、ExtendUnique、Extend、Sort、CopyTo、Join、Clear 等等,這可以參考 boo 源碼 src/boo.lang/List.cs。
Slicing
[编辑]Slicing(切割)。
Slicing 非常簡單,而且適用於字串、串列和陣列。 語法是這樣的:var[啟始:結束]。啟始與結束指不指定都沒關係,但必須是整數(負整數也行)。 只要取得一個元素的話,用 var[位置] 指定位置,如果是字串,會傳回字元﹔如果是串列,會傳回物件﹔如果是陣列,會傳回陣列裡該元素的型別。 Slicing 以 0 為基底,所以 0 是第一個,1 則是第二個,以此類推。
// slicing
list = List(range(10))
print list
print list[:5] // 取前五個
print list[2:5] // 取第三個元素到第五個元素,但不包含第六個元素,也就是不包含 list[5]
print list[5:] // 取第六個元素之後的全部
print list[:-2] // 後面數過來的兩個元素都不要,取前面八個
print list[-4:-2] // 只取從後面數過來的第四個,到從後面數過來的第二個
print list[5] // 只取第六個
print list[-8] // 只取從後面數過來第八個
print '---'
str = 'abcdefghij'
print str
print str[:5] // 取前五個
print str[2:5] // 取第三個元素到第五個元素,但不包含第六個元素,也就是不包含 list[5]
print str[5:] // 取第六個元素之後的全部
print str[:-2] // 後面數過來的兩個元素都不要,取前面八個
print str[-4:-2] // 只取從後面數過來的第四個,到從後面數過來的第二個
print str[5] // 只取第六個
print str[-8] // 只取從後面數過來第八個
輸出結果
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4] [2, 3, 4] [5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7] [6, 7] 5 2 --- abcdefghij abcde cde fghij abcdefgh gh f c
我希望你能了解,Slicing 非常有用,可以讓你用很具可讀性的語法來取得字串、串列或陣列裡的其中一部分。
陣列
[编辑]與串列不同,陣列無法改變大小,只能被切割(Slice)。
陣列可以用三種方法來定義:
- 使用小括號:()
- 如果有 0 個元素,可以這樣定義:(,)
- 如果有 1 個元素,可以這樣定義:(member,)
- 如果有 2 個元素,可以這樣定義:(one, two)
- 將串列或 IEnumerator 傳入 array 函數。
- 將型別與大小傳入 array():array(type, size)
// 陣列
print((0, 'alpha', 4.5, char('d')))
print array('abcdefghij')
l = array(range(5))
print l
l[2] = 5
print l
l[3] = 'banana'
輸出結果
(0, alpha, 4.5, d) (a, b, c, d, e, f, g, h, i, j) (0, 1, 2, 3, 4) (0, 1, 5, 3, 4) ERROR: Cannot convert 'System.String' to 'System.Int32'.
陣列不像串列,裡面的元素並不一定要是 object。通常會有明確的型別,以 array(range(5)) 這個例子來說,會自動產生一個整數的陣列。
譯註:以上面的例子,(0,'alpha',4.5,char('d')) 則是一個 object 的陣列,裡面的元素型別都是 object。
轉換串列為陣列
[编辑]當你建立了一個裡面都是整數的串列,而你想轉換成陣列時,你必須明確指定要轉成什麼型別。
// 串列轉陣列
list = []
for i in range(5):
list.Add(i)
print list
a = array(int, list)
print a
a[2] += 5
print a
list[2] += 5
輸出結果
[0] [0, 1] [0, 1, 2] [0, 1, 2, 3] [0, 1, 2, 3, 4] (0, 1, 2, 3, 4) (0, 1, 7, 3, 4) ERROR: Operator '+' cannot be used with a left-hand side of type 'System.Object' and a right-hand side of type 'System.Int32'
list[2]+=5 會出現錯誤,因為 List 裡每個元素的型別都是 object,並非整數,雖然實際上放的是整數。
譯註:如果你想要萃取 List 裡屬於某個型別的元素,可以先使用 Collect:
def IsInteger( o as object ):
i=0
if o.GetType()==i.GetType():
return true
else:
return false
list = [ 1, 2, 3, 4, 5, 'a1', 'a2', 'a3', 'a4', 'a5' ]
a = list.Collect( IsInteger ).ToArray( int ) // 或者這樣寫 a = array(int, list.Collect( IsInteger ) )
print a
轉型
[编辑]串列裡僅能存放物件型別,你可以將之轉換為它們真正的型別,然後操作它們。 假定你想轉換為不適合的型別,例如字串轉為整數,Boo 將會告知錯誤。
有兩種方法可以將物件轉換為其他資料型別:
- 使用 var as <type> 的語法
- 使用 cast(<type>, var) 函數
// casting example
list = List(range(5))
print list
for item in list:
print cast(int, item) * 5
print '---'
for item as int in list:
print item * item
輸出結果
0 5 10 15 20 --- 0 1 4 9 16
即將出現的新功能:泛型(Generics) 泛型(Generics),將會是 .Net framework 2.0 的一部分,2.0將允許你建立一個以特定型別為基礎的串列。所以,很快地,你將省掉轉換串列項目型別的功夫。
Hashes
[编辑]Hashes 在某些語言裡被稱為"字典",它與串列非常類似,最大的差別在於 Hashes 可以藉著整數或字串的鍵值快速存取物件。
在 Boo 裡,可以用三種方法來定義 Hashes:
- 使用大括號 {}
- 建立類別並使其繼承 IEnumerator 或 IDictionary。
- 使用 Hash 函數 (譯註:原來並沒有提及此項,而只有在下面的範例中說明。)
// hash example
hash = {'a': 1, 'b': 2, 'monkey': 3, 42: 'the answer'}
print hash['a']
print hash[42]
print '---'
for item in hash:
print item.Key, '=>', item.Value
# 同樣的 hash 也可以從串列裡產生出來,使用 Hash():
ll = [ ('a',1), ('b',2), ('monkey',3), (42, "the answer") ]
hash = Hash(ll)
輸出結果
1 the answer --- a => 1 b => 2 monkey => 3 42 => the answer
泛型(Generic)
[编辑]Boo 在 .Net framework 2.0 推出後,也加入了對泛型的支援,語法是這樣子的(參考自:Boo Generic Syntax):
// 泛型(Generic) MyType of MyGenericParam // 有多個泛型(Generic)參數時 MyType[of X,Y,Z]
來看看例子:
import System.Collections.Generic
l = List[of int]()
l.Add( 10 )
l.Add( 20 )
for i in l:
print i
l.Add( "hello" ) // 這行將會發生錯誤,告訴你不可以加入字串型別
// 範例取自原始碼 tests/testcase/net2/generics/generic-array-1.boo
import System.Collections.Generic
arr = array(List[of int], 3) // 產生三個元素的陣列,元素的型別是以整數為基礎的串列
arr[0] = List[of int]() // 將第一個元素初始化
arr[0].Add( 1 ) // 加入一個整數
print arr[0].Count // 得到 1
泛型也可以作用於方法(method)上,也就是所謂的泛型方法(Generic method):
// 範例取自原始碼 tests/testcase/net2/generics/generic-method-1.boo
def Method[of T](parameter as T):
print parameter.GetType()
print parameter
Method[of int](42)
練習
[编辑]- 產生一個包含 1000 個 fibonacci 數的串列 (看看你能不能在四行之內完成它!)