亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

習(xí)題 49: 創(chuàng)建句子?

從我們這個(gè)小游戲的詞匯掃描器中,我們應(yīng)該可以得到類似下面的列表:

>>> from ex48 import lexicon
>>> print lexicon.scan("go north")
[('verb', 'go'), ('direction', 'north')]
>>> print lexicon.scan("kill the princess")
[('verb', 'kill'), ('stop', 'the'), ('noun', 'princess')]
>>> print lexicon.scan("eat the bear")
[('verb', 'eat'), ('stop', 'the'), ('noun', 'bear')]
>>> print lexicon.scan("open the door and smack the bear in the nose")
[('error', 'open'), ('stop', 'the'), ('noun', 'door'), ('error', 'and'),
('error', 'smack'), ('stop', 'the'), ('noun', 'bear'), ('stop', 'in'),
('stop', 'the'), ('error', 'nose')]
>>>

現(xiàn)在讓我們把它轉(zhuǎn)化成游戲可以使用的東西,也就是一個(gè) Sentence 類。

如果你還記得學(xué)校學(xué)過的東西的話,一個(gè)句子是由這樣的結(jié)構(gòu)組成的:

主語(Subject) + 謂語(動(dòng)詞 Verb) + 賓語(Object)

很顯然實(shí)際的句子可能會(huì)比這復(fù)雜,而你可能已經(jīng)在英語的語法課上面被折騰得夠嗆了。我們的目的,是將上面的元組列表轉(zhuǎn)換為一個(gè) Sentence 對(duì)象,而這個(gè)對(duì)象又包含主謂賓各個(gè)成員。

匹配(Match)和窺視(Peek)?

為了達(dá)到這個(gè)效果,你需要四樣工具:

  1. 循環(huán)訪問元組列表的方法,這挺簡(jiǎn)單的。
  2. 匹配我們的主謂賓設(shè)置中不同種類元組的方法。
  3. 一個(gè)“窺視”潛在元組的方法,以便做決定時(shí)用到。
  4. 跳過(skip)我們不在乎的內(nèi)容的方法,例如形容詞、冠詞等沒有用處的詞匯。

我們使用 peek 函數(shù)來查看元組列表中的下一個(gè)成員,做匹配以后再對(duì)它做下一步動(dòng)作。讓我們先看看這個(gè) peek 函數(shù):

def peek(word_list):
    if word_list:
        word = word_list[0]
        return word[0]
    else:
        return None

很簡(jiǎn)單。再看看 match 函數(shù):

def match(word_list, expecting):
    if word_list:
        word = word_list.pop(0)

        if word[0] == expecting:
            return word
        else:
            return None
    else:
        return None

還是很簡(jiǎn)單,最后我們看看 skip 函數(shù):

def skip(word_list, word_type):
    while peek(word_list) == word_type:
        match(word_list, word_type)

以你現(xiàn)在的水平,你應(yīng)該可以看出它們的功能來。確認(rèn)自己真的弄懂了它們。

句子的語法?

有了工具,我們現(xiàn)在可以從元組列表來構(gòu)建句子(Sentence)對(duì)象了。我們的處理流程如下:

  1. 使用 peek 識(shí)別下一個(gè)單詞。
  2. 如果這個(gè)單詞和我們的語法匹配,我們就調(diào)用一個(gè)函數(shù)來處理這部分語法。假設(shè)函數(shù)的名字叫 parse_subject 好了。
  3. 如果語法不匹配,我們就 raise 一個(gè)錯(cuò)誤,接下來你會(huì)學(xué)到這方面的內(nèi)容。
  4. 全部分析完以后,我們應(yīng)該能得到一個(gè) Sentence 對(duì)象,然后可以將其應(yīng)用在我們的游戲中。

演示這個(gè)過程最簡(jiǎn)單的方法是把代碼展示給你讓你閱讀,不過這節(jié)習(xí)題有個(gè)不一樣的要求,前面是我給你測(cè)試代碼,你照著寫出程序來,而這次是我給你的程序,而你要為它寫出測(cè)試代碼來。

以下就是我寫的用來解析簡(jiǎn)單句子的代碼,它使用了 ex48.lexicon 這個(gè)模組。

class ParserError(Exception):
    pass


class Sentence(object):

    def __init__(self, subject, verb, object):
        # remember we take ('noun','princess') tuples and convert them
        self.subject = subject[1]
        self.verb = verb[1]
        self.object = object[1]


def peek(word_list):
    if word_list:
        word = word_list[0]
        return word[0]
    else:
        return None


def match(word_list, expecting):
    if word_list:
        word = word_list.pop(0)

        if word[0] == expecting:
            return word
        else:
            return None
    else:
        return None


def skip(word_list, word_type):
    while peek(word_list) == word_type:
        match(word_list, word_type)


def parse_verb(word_list):
    skip(word_list, 'stop')

    if peek(word_list) == 'verb':
        return match(word_list, 'verb')
    else:
        raise ParserError("Expected a verb next.")


def parse_object(word_list):
    skip(word_list, 'stop')
    next = peek(word_list)

    if next == 'noun':
        return match(word_list, 'noun')
    if next == 'direction':
        return match(word_list, 'direction')
    else:
        raise ParserError("Expected a noun or direction next.")


def parse_subject(word_list, subj):
    verb = parse_verb(word_list)
    obj = parse_object(word_list)

    return Sentence(subj, verb, obj)


def parse_sentence(word_list):
    skip(word_list, 'stop')

    start = peek(word_list)

    if start == 'noun':
        subj = match(word_list, 'noun')
        return parse_subject(word_list, subj)
    elif start == 'verb':
        # assume the subject is the player then
        return parse_subject(word_list, ('noun', 'player'))
    else:
        raise ParserError("Must start with subject, object, or verb not: %s" % start)

關(guān)于異常(Exception)?

你已經(jīng)簡(jiǎn)單學(xué)過關(guān)于異常的一些東西,但還沒學(xué)過怎樣拋出(raise)它們。這節(jié)的代碼演示了如何 raise 前面定義的 ParserError。注意 ParserError 是一個(gè)定義為 Exception 類型的 class。另外要注意我們是怎樣使用 raise 這個(gè)關(guān)鍵字來拋出異常的。

你的測(cè)試代碼應(yīng)該也要測(cè)試到這些異常,這個(gè)我也會(huì)演示給你如何實(shí)現(xiàn)。

你應(yīng)該測(cè)試的東西?

為《習(xí)題 49》寫一個(gè)完整的測(cè)試方案,確認(rèn)代碼中所有的東西都能正常工作,其中異常的測(cè)試——輸入一個(gè)錯(cuò)誤的句子它會(huì)拋出一個(gè)異常來。

使用 assert_raises 這個(gè)函數(shù)來檢查異常,在 nose 的文檔里查看相關(guān)的內(nèi)容,學(xué)著使用它寫針對(duì)“執(zhí)行失敗”的測(cè)試,這也是測(cè)試很重要的一個(gè)方面。從 nose 文檔中學(xué)會(huì)使用 assert_raises,以及一些別的函數(shù)。

寫完測(cè)試以后,你應(yīng)該就明白了這段程序的工作原理,而且也學(xué)會(huì)了如何為別人的程序?qū)憸y(cè)試代碼。 相信我,這是一個(gè)非常有用的技能。

加分習(xí)題?

  1. 修改 parse_ 函數(shù)(方法),將它們放到一個(gè)類里邊,而不僅僅是獨(dú)立的方法函數(shù)。這兩種程序設(shè)計(jì)你喜歡哪一種呢?
  2. 提高 parser 對(duì)于錯(cuò)誤輸入的抵御能力,這樣即使用戶輸入了你預(yù)定義語匯之外的詞語,你的程序也能正常運(yùn)行下去。
  3. 改進(jìn)語法,讓它可以處理更多的東西,例如數(shù)字。
  4. 想想在游戲里你的 Sentence 類可以對(duì)用戶輸入做哪些有趣的事情。