BOO入门/容器与转型

BOO入门 > 容器与转型 (上一章:流程控制-回圈 下一章:运算子)


串列

编辑
  定义 串列(List):持有可变数量物件的链结串列(容器)。

串列可以很容易地增/删里面的元素。

// 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

从例子可以看到,串列很有弹性,也很方便。 定义的方法有两种:

  1. 使用中括号 []
  2. 将列举子(IEnumator)或阵列传入 List 函数中。

译注

编辑
  1. 例子的第4行 List( range(5) ) 就是将列举子(IEnumerator)传入,range() 传回的是一个 IEnumerator﹔ List( ( 1,2,3) ) 则是将阵列传入,关于阵列的部份,下节会提到。
  2. 串列无法事先指定所要使用的型别,里面的每个元素都是 object。因此转换成阵列时,你需要确定每个元素都是你要的型别才能转换,否则 Boo 会告知错误。
  3. 由于串列提供了 Push()、Pop() 函数,所以你可以把它当 Stack 来使用。
  4. 串列可以使用 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)。

阵列可以用三种方法来定义:

  1. 使用小括号:()
    • 如果有 0 个元素,可以这样定义:(,)
    • 如果有 1 个元素,可以这样定义:(member,)
    • 如果有 2 个元素,可以这样定义:(one, two)
  2. 将串列或 IEnumerator 传入 array 函数。
  3. 将型别与大小传入 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 将会告知错误。

有两种方法可以将物件转换为其他资料型别:

  1. 使用 var as <type> 的语法
  2. 使用 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

编辑
  定义 Hash(杂凑):经过索引的串列,而不只是在固定范围里的循序整数,可以透过键值快速取得所需的物件。

Hashes 在某些语言里被称为"字典",它与串列非常类似,最大的差别在于 Hashes 可以藉著整数或字串的键值快速存取物件。

在 Boo 里,可以用三种方法来定义 Hashes:

  1. 使用大括号 {}
  2. 建立类别并使其继承 IEnumerator 或 IDictionary。
  3. 使用 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)

练习

编辑
  1. 产生一个包含 1000 个 fibonacci 数的串列 (看看你能不能在四行之内完成它!)