小段落

- 5.1 列(Lists)(續)
  - 5.1.1 把列(Lists)當作堆積(Stacks)使用
  - 5.1.2 把列(Lists)當作佇列(Queues)使用
  - 5.1.3 功能式程式設計工具
  - 5.1.4 傳回整個列(List Comprehensions)
- 5.2 `del` 敘述
- 5.3 Tuples(固定有序列)及Sequences(有序列)
- 5.4 Dictionaries(字典)
- 5.5 條件(續)
- 5.6 Sequences(有序列)及其他資料型態的比較

#
5. 資料結構

這一章討論的內容有些你在之前已經看過，但我們會更深入的討論。另外，我們也會介紹一些新的東西。

#
5.1 列(Lists)(續)

列(list)這個資料型態有一些方法可以使用，底下我們就列出來一些常用的方法：

`append(x)`
  在list的尾端加入一個成員，也可以用這個方法來寫 `a[len(a):] = [x]` 。

`extend(L)`
  接受一個新的list的參數，然後把它加入到目前這個list的尾端，也可以寫作 `a[len(a):] = L` 。

`insert(i, x)`
  在某個特定的位置加入一個成員。第一個參數是要加入的位置的index，所以 `a.insert(0, x)` 會加入在list的最前端，而 `a.insert(len(a), x)` 會在最後端加入，相等於 `a.append(x)` 。

`remove(x)`
  拿掉第一個其值相等於 `x`. 的成員。如果整個list都沒有這個成員，那就會得到一個錯誤(error)。

`pop([i])`
  從一個list中拿掉某個位置的成員，並且傳回這個被拿掉的成員。如果沒有傳入位置的index的話， `a.pop()` 會傳回這個list的最一個成員，同樣的這個成為會被從這個list之中拿掉。

`index(x)`
  傳回第一個其值相等於 `x` 的成員之位置(index)，如果整個list都沒有這個成員，那就會得到一個錯誤(error)。

`count(x)`
  傳回在整個list裡面， `x` 出現了多少次。

`sort()`
  針對list裡面的成員做排序。

`reverse()`
  反轉整個list裡面成員的位置。

底下的這個例子使用了大部分的lsit的方法(method)：

      >>> a = [66.6, 333, 333, 1, 1234.5]
      >>> print a.count(333), a.count(66.6), a.count('x')
      2 1 0
      >>> a.insert(2, -1)
      >>> a.append(333)
      >>> a
      [66.6, 333, -1, 333, 1, 1234.5, 333]
      >>> a.index(333)
      1
      >>> a.remove(333)
      >>> a
      [66.6, -1, 333, 1, 1234.5, 333]
      >>> a.reverse()
      >>> a
      [333, 1234.5, 1, 333, -1, 66.6]
      >>> a.sort()
      >>> a
      [-1, 1, 66.6, 333, 333, 1234.5]

##
5.1.1 把列(Lists)當作堆積(Stacks)使用

由於有這些好用的方法，把列(list)當作堆積(stack)來使用是一件容易的事。Stacks的最後一個加入的成員是第一個被取出來的成員(後進先出``last-in, first-out''法則)。要在stack的最頂端加入一個成員可以使用 `append()` ，要從stack的最頂端取出一個成員可以用 `pop()` (不須加入參數)。例子如下：

      >>> stack = [3, 4, 5]
      >>> stack.append(6)
      >>> stack.append(7)
      >>> stack
      [3, 4, 5, 6, 7]
      >>> stack.pop()
      7
      >>> stack
      [3, 4, 5, 6]
      >>> stack.pop()
      6
      >>> stack.pop()
      5
      >>> stack
      [3, 4]

##
5.1.2 把列(Lists)當作佇列(Queues)使用

你也可以很方便的拿list來當作佇列(queues)來使用。Queues的特點是第一個加入的成員會是第一個被取出的成員(先進先出``first-in, first-out''法則)。要在queue的後端加入一個成員可以使用 `append()` ，要從queue的最前端取出一個成員可以使用 use `pop()` ，記得參數是 `0` 。例子如下：

      >>> queue = ["Eric", "John", "Michael"]
      >>> queue.append("Terry")           # Terry arrives
      >>> queue.append("Graham")          # Graham arrives
      >>> queue.pop(0)
      'Eric'
      >>> queue.pop(0)
      'John'
      >>> queue
      ['Michael', 'Terry', 'Graham']

##
5.1.3 功能式程式設計工具

有三個與list合用非常有用的內建工具函式： `filter()`, `map()`, 以及 `reduce()` 。

"`filter( function, sequence)`" 這個函式會傳回 一個sequence (如果可能的話其成員為同一資料型態)，這個sequence裡面的成員都是將 sequence 裡面的的成員，一一傳入到 `function( item)` 所代表的函式後，傳回值為true的成員所組合而成。這個函式對於傳入的 sequence 有過濾的效果，如下例所示：

      >>> def f(x): return x % 2 != 0 and x % 3 != 0
      ...
      >>> filter(f, range(2, 25))
      [5, 7, 11, 13, 17, 19, 23]

"`map( function, sequence)`" 會針對 sequence 裡的各個成員呼叫 `function(item)` ，然後傳回個別成員呼叫之後傳回的結果。舉例來說，要計算一連串的立方值，我們可以如此做：

      >>> def cube(x): return x*x*x
      ...
      >>> map(cube, range(1, 11))
      [1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

我們也可以傳入不只一個sequence。如果傳入多個sequence的話，第一個函式名稱的參數要是能夠處理多個參數的函式，然後系統會把各個sequence相對應的成員拿出來放入函式之中(如果兩個sequence長度不相等的話，不足的會用 `None` 來補足)。如果第一個函式名稱參數為 `None` 的話，所呼叫的函式就僅僅是傳回其所傳入的參數。

綜合以上的兩個特性，我們可以使用 "`map(None, list1, list2)`" 這一個工具函式來方便的轉換兩個sequence成為一個成對的成員組合的sequence。請看例子：

      >>> seq = range(8)
      >>> def square(x): return x*x
      ...
      >>> map(None, seq, map(square, seq))
      [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25), (6, 36), (7, 49)]

"`reduce( func, sequence)`" 會利用 sequence 的前兩個成員當參數呼叫 func ，然後所得的傳回值再與下一個成員當參數傳入 func ，一直到整個 sequence 結束。下面的例子計算1到10的總和：

      >>> def add(x,y): return x+y
      ...
      >>> reduce(add, range(1, 11))
      55

如果在 sequence 裡面只有一個成員的話，這個成員的值就會直接傳回。如果在 sequence 裡面沒有任何成員的話，會造成一個例外狀況(exception)。

我們也可以加入第三個參數來當作開始的值，如此當傳入的 sequence 是空的話，就可以使用這個開始值。如果是正常的sequencde的話，開始值會先跟第一個成員被傳入當作呼叫 func 的參數，其傳回值再跟第二個成員傳入 func ，依此類推。請看下例：

      >>> def sum(seq):
      ...     def add(x,y): return x+y
      ...     return reduce(add, seq, 0)
      ...
      >>> sum(range(1, 11))
      55
      >>> sum([])
      0

## 5.1.4 傳回整個列 (List Comprehensions)

List comprehensions提供了一個製造list簡潔的方法，而不用使用 `map()`, `filter()` 以及/或者 `lambda` 形式。其結果也比使用以上的方法來做出list要來的清楚易懂。list comprehension通常是一個expression跟著是一個 `for` 的語句，然後是零個或多個 `for` 或是 `if` 語句。其傳回的list是一個由在 `for` 及 `if` 語句條件下執行expression的結果。如果expression的結果是一個tuple，就必須用括號"( )"括起來。

      >>> freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']
      >>> [weapon.strip() for weapon in freshfruit]
      ['banana', 'loganberry', 'passion fruit']
      >>> vec = [2, 4, 6]
      >>> [3*x for x in vec]
      [6, 12, 18]
      >>> [3*x for x in vec if x > 3]
      [12, 18]
      >>> [3*x for x in vec if x < 2]
      []
      >>> [{x: x**2} for x in vec]
      [{2: 4}, {4: 16}, {6: 36}]
      >>> [[x,x**2] for x in vec]
      [[2, 4], [4, 16], [6, 36]]
      >>> [x, x**2 for x in vec]	# error - parens required for tuples
        File "<stdin>", line 1
          [x, x**2 for x in vec]
                     ^
      SyntaxError: invalid syntax
      >>> [(x, x**2) for x in vec]
      [(2, 4), (4, 16), (6, 36)]
      >>> vec1 = [2, 4, 6]
      >>> vec2 = [4, 3, -9]
      >>> [x*y for x in vec1 for y in vec2]
      [8, 6, -18, 16, 12, -36, 24, 18, -54]
      >>> [x+y for x in vec1 for y in vec2]
      [6, 5, -7, 8, 7, -5, 10, 9, -3]

#
5.2 `del` 敘述

`del` 敘述可以讓你輕鬆的去掉在list當中某一個位置(index)的成員。這個敘述也可以用切割(slice)的方法來去掉某一段的成員(在之前我們必須藉著設定某個slice為空list來達成同樣的效果)。請看下例：

      >>> a
      [-1, 1, 66.6, 333, 333, 1234.5]
      >>> del a[0]
      >>> a
      [1, 66.6, 333, 333, 1234.5]
      >>> del a[2:4]
      >>> a
      [1, 66.6, 1234.5]

`del` 也可以用來去掉整個變數：

      >>> del a

如果你在去掉之後還繼續使用這個變數名稱的話，就會得到一個錯誤 (除非你之後再設定另外一個值給它)。我們稍後會繼續看到使用 `del` 的例子。

#
5.3 Tuples(固定有序列)及Sequences(有序列)

我們之前所討論的lists以及字串(strings)有很多的共通點，例如可以用index來定位置，可以切出其中的某一段(slicing)等等。事實上，list及字串都是 sequence 這個資料型態的特例。由於Python是一個可以不斷進步的語言，其他的sequence資料型態有可能會陸續的加入。我們就來看另外一種標準的sequence資料型態：固定有序列( tuple )。

一個tuple是由特定數目的值所組成，其成員與成員之間以逗號分開。舉例如下：

      >>> t = 12345, 54321, 'hello!'
      >>> t[0]
      12345
      >>> t
      (12345, 54321, 'hello!')
      >>> # Tuples may be nested:
      ... u = t, (1, 2, 3, 4, 5)
      >>> u
      ((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))

如同在前面例子所見到的，tuples輸出的結果都會包含在括弧之中。所以，巢狀tuple(tuple之中有tuple)可以被清楚的區分出來。Tuple在輸入的時候可以有括弧也可以沒有，通常我們都會加上括弧(特別是用在在複雜的expression之中)。

Tuples有很多種用途，例如(x, y)座標，從資料庫取出的員工的資料庫記錄等等。Tuples跟字串一樣都是不可改變(immutable)的：我們不能單獨的設定一個tuple裡面的個別成員(雖然我們可以用切割及連結(concaatenation)來達到同樣的效果)。我們也可以做出可以改變成員的tuple來，例如list。

有一個特殊的情況就是只包含0個或1個成員的tuple：要創造這樣的一個tuple，我們必須在語法上有一些的變化。空的tuple的表示方法是一對空的括弧，只有一個成員的tuple表示方法是在成員後面加上逗點(不能只是用括弧把一個成員括起來)。雖然有點奇怪，但是蠻有用的。請看例子：

      >>> empty = ()
      >>> singleton = 'hello',    # <-- note trailing comma
      >>> len(empty)
      0
      >>> len(singleton)
      1
      >>> singleton
      ('hello',)

`t = 12345, 54321, 'hello!'` 這個敘述是一個tuple包裝( tuple packing )的例子： `12345` , `54321` 以及 `'hello!'` 這三個值都被包裝放在一個tuple裡面了。我們也可以使用相反的操作方式，例如：

      >>> x, y, z = t

這個動作叫做打開sequence( sequence unpacking )。Sequence unpacking的動作需要在設定符號左邊有一串的變數，其數目應與右邊sequence的成員數目相同。值得注意的是，多重設定(a, b = 1, 2)其實只是tuple packing以及sequence unpacking的結合罷了！

有一個不太對稱的地方：packing的動作永遠結果是一個tuple，但是unpacking可以針對各種不同的sequence來做。

#
5.4 Dictionaries(字典)

另外一個在Python當中很好用的內建資料型態是字典( dictionary )。Dictionary有的時候在別的程式語言裡面也叫做連結記憶( ``associative memories'' )或者是連結陣列( ``associative arrays'' )。不同於sequence是由一連串的數字來做index，dictionary用一個特殊的不可改變的(immutable)鑰( keys 來當作其 index。字串及數字都不能被改變，所以都可以來當作dictionary的key。Tuple如果只含有字串，數目字，以及其他tuple的話也可以當作key。如果tuple裡面有包含任何可改變的(mutable)的物件的話(包括直接或間接)，就不能當作key來使用。List不能當作key，因為list的成員可以被改變(你可以用 `append()` 以及 `extend()` 之類的方法，或是切割(slicing) 或 index 來設定list的個別成員)。

我們最好把dictionary想像成一個沒有順序的 key: value 成對的組合。唯一的條件是，在dictionary裡面key的值必須是唯一不重複的。最簡單的dictionary就是一對空的中括弧： `{}` 。在中括弧裡面放入由逗號分開的key:value對，就成了dictionary裡面的成員。這也是當dictionary被印到輸出時的標準格式。

我們可以對dictionary做一些事，包括加入一個帶有key的值、或者是用key來找一個特殊的值。我們也可以用 `del` 來刪去一對key:value的成員。如果你試圖存一對key:value但是這個key已經被使用了的話，原來的那一個value的值就會被蓋過。如果你想用一個不存在的key來找出某一個成員的話，你會得到一個error。

使用 `keys()` 這一個dictionary的方法我們可以得到一個由所有的key值組成的list，其順序是隨機沒有次序的(如果你想要排序的話，只要針對這一個得到的list來呼叫其 `sort()` 方法就可以了)。要檢查某個key是不是存在的話，你可以使用 `has_key()` 這一個method來做檢查。

底下是一個有關dictionary的小例子：

      >>> tel = {'jack': 4098, 'sape': 4139}
      >>> tel['guido'] = 4127
      >>> tel
      {'sape': 4139, 'guido': 4127, 'jack': 4098}
      >>> tel['jack']
      4098
      >>> del tel['sape']
      >>> tel['irv'] = 4127
      >>> tel
      {'guido': 4127, 'irv': 4127, 'jack': 4098}
      >>> tel.keys()
      ['guido', 'irv', 'jack']
      >>> tel.has_key('guido')
      1

#
5.5 條件(續)

在之前所談到的 `while` 及 `if` 裡面的條件敘述，除了一般的比較之外也可以包含其他的運算。

我們可以用 `in` 以及 `not in` 來檢查某個值是否出現(或沒出現)在某個sequence裡面。我們也可以使用 `is` 以及 `is not` 來檢查兩個物件是否事實上指的是相同的一個物件(這只有跟像是list一樣可變的物件有關)。所有的比較運算的運算優先次序都是一樣的，都比所有的數字運算要來的低。

比較運算是可以連起來的：像是 `a < b == c` 就是試驗是否 `a` 比 `b` 小，以及 `b` 和 `c` 是否相等。

比較運算也可以用 `and` 以及 `or` 等boolean運算來連結起來，其比較的結果(或其他boolean運算的結果)也可以用 `not` 來得到相反(negated)的結果。在這些運算裡， `not` 有最高的優先次序， `or` 的優先次序最低，但是它們所有的優先次序都比比較運算來的低。所以， `A and not B or C` 其實相等於 `(A and (not B)) or C` 。當然，最好適時的使用括弧來幫助你表達你真正想要的組合。

`and` 以及 `or` 這兩個boolean運算元也可以稱做有捷徑的運算元( shortcut operators)：它們的evaluated的次序都是由左而右，而且一但已經可以決定其運算的結果，就不會再繼續的做下去。也就是說如果 `A` 以及 `C` 都是 true 而 `B` 是false的話， `A and B and C` 並不會evaluate C 這個expression。一般來說這些shortcut operator 的傳回值如果不是當作boolean而是當作一般的值來用的話，其傳回值會是最後一個被evaluate的expression的值。

我們也可以把一個比較運算，或是 boolean運算的結果設定給一個變數，其例子如下：

      >>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
      >>> non_null = string1 or string2 or string3
      >>> non_null
      'Trondheim'

值得注意的是不像是C，在Python裡面設定(assignment)不能夠放在expression裡面。C的程式設計師也許會針對此點抱怨，但是這樣的好處是可以避免一些常見的把設定( `=` )及等於( `==` )弄混淆的情形

#
5.6 Sequences(有序列)及其他資料型態的比較

Sequence 物件可以和其他的相同資料型態的sequence 物件相比較，其比較方法是依照所謂的 lexicographical 順序(lexicographical ordering)。首先是兩個sequence的第一個成員互相比較，如果比較有大小之別的話就此決定其相對大小，若是相等的話就再比較下一個成員的大小，餘此類推直到sequence的結束。如果兩個要相比較的成員本身也是一個sequence的話，同樣的條件可以繼續遞迴的使用在這兩個sequence之上。如果這兩個sequence的所有成員都相等的話，我們就說這兩個成員是相等的。如果某一個sequence是另一個sequence的一部份的話，較短的那一個sequence就是較小的。字串的Lexicographical順序用的是個別字元的ASCII碼的順序。底下是一些同一資料型態的sequence的比較例子：

      (1, 2, 3)              < (1, 2, 4)
      [1, 2, 3]              < [1, 2, 4]
      'ABC' < 'C' < 'Pascal' < 'Python'
      (1, 2, 3, 4)           < (1, 2, 4)
      (1, 2)                 < (1, 2, -1)
      (1, 2, 3)             == (1.0, 2.0, 3.0)
      (1, 2, ('aa', 'ab'))   < (1, 2, ('abc', 'a'), 4)

值得注意的是，我們也可以 比較兩個不同資料型態的物件，而其結果是依其資料型態的名稱來決定的。所以所有的list都比字串還要來的小(因為list小於string)，所有的string也都比tuple還要小。至於數值的資料型態則是由其數值大小來決定其大小，所以0等於0.0的，其餘按此類推。 5.1

#### Footnotes

... 其餘按此類推。5.1
  你不應該完全倚賴這些比較不同資料型態的規則，因為這些規則是尚未確定的，以後的Python版本也有可能會再做更動。

請看關於此文件… 裡面有關如何給我們建議的說明。