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 数的串列 (看看你能不能在四行之内完成它!)