网站seo优化免费,zhicms系统wordpress,什么是网络营销中的kpi,WordPress留言表单仿制习题 48: 更复杂的用户输入
你的游戏可能一路跑得很爽#xff0c;不过你处理用户输入的方式肯定让你不胜其烦了。每一个房间都需要一套自己的语句#xff0c;而且只有用户完全输入正确后才能执行。你需要一个设备#xff0c;它可以允许用户以各种方式输入语汇。例如下面的机…习题 48: 更复杂的用户输入¶
你的游戏可能一路跑得很爽不过你处理用户输入的方式肯定让你不胜其烦了。每一个房间都需要一套自己的语句而且只有用户完全输入正确后才能执行。你需要一个设备它可以允许用户以各种方式输入语汇。例如下面的机种表述都应该被支持才对
open door
open the door
go THROUGH the door
punch bear
Punch The Bear in the FACE
也就是说如果用户的输入和常用英语很接近也应该是可以的而你的游戏要识别出它们的意思。为了达到这个目的我们将写一个模组专门做这件事情。这个模组里边会有若干个类它们互相配合接受用户输入并且将用户输入转换成你的游戏可以识别的命令。
英语的简单格式是这个样子的
单词由空格隔开。
句子由单词组成。
语法控制句子的含义。
所以最好的开始方式是先搞定如何得到用户输入的词汇并且判断出它们是什么。
我们的游戏语汇¶
我在游戏里创建了下面这些语汇
表示方向: north, south, east, west, down, up, left, right, back.
动词: go, stop, kill, eat.
修饰词: the, in, of, from, at, it
名词: door, bear, princess, cabinet.
数词: 由 0-9 构成的数字。
说到名词我们会碰到一个小问题那就是不一样的房间会用到不一样的一组名词不过让我们先挑一小组出来写程序以后再做改进把。
如何断句¶
我们已经有了词汇表为了分析句子的意思接下来我们需要找到一个断句的方法。我们对于句子的定义是“空格隔开的单词”所以只要这样就可以了
stuff raw_input( )
words stuff.split()
目前做到这样就可以了不过这招在相当一段时间内都不会有问题。
语汇元组¶
一旦我们知道了如何将句子转化成词汇列表剩下的就是逐一检查这些词汇看它们是什么类型。为了达到这个目的我们将用到一个非常好使的 Python 数据结构叫做”元组(tuple)”。元组其实就是一个不能修改的列表。创建它的方法和创建列表差不多成员之间需要用逗号隔开不过方括号要换成圆括号 ()
first_word (direction, north)
second_word (verb, go)
sentence [first_word, second_word]
这样我们就创建了一个 (TYPE, WORD) 组让你识别出单词并且对它执行指令。
这只是一个例子不过最后做出来的样子也差不多。你接受用户输入用 split
将其分隔成单词列表然后分析这些单词识别它们的类型最后重新组成一个句子。
扫描输入¶
现在你要写的是词汇扫描器。这个扫描器会将用户的输入字符串当做参数然后返回由多个 (TOKEN, WORD) 组成的一个列表这个列表实现类似句子的功能。如果一个单词不在预定的词汇表中那它返回时 WORD 应该还在但 TOKEN 应该设置成一个专门的错误标记。这个错误标记将告诉用户哪里出错了。
有趣的地方来了。我不会告诉你这些该怎样做但我会写一个“单元测试(unit test)”而你要把扫描器写出来并保证单元测试能够正常通过。
“异常”和数字¶
有一件小事情我会先帮帮你那就是数字转换。为了做到这一点我们会作一点弊使用“异常(exceptions)”来做。“异常”指的是你运行某个函数时得到的错误。你的函数在碰到错误时就会“提出(raise)”一个“异常”然后你就要去处理(handle)这个异常。假如你在Python 里写了这些东西
~/projects/simplegame $ python
Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41)
[GCC 4.4.3] on linux2
Type help, copyright, credits or license for more information.
int(hell)
Traceback (most recent call last):
File , line 1, in
ValueError: invalid literal for int() with base 10: hell这个 ValueError 就是 int() 函数抛出的一个异常。因为你给 int() 的参数不是一个数字。 int() 函数其实也可以返回一个值来告诉你它碰到了错误不过由于它只能返回整数值所以很难做到这一点。它不能返回 -1因为这也是一个数字。
int() 没有纠结在它“究竟应该返回什么”上面而是提出了一个叫做 ValueError
的异常然后你只要处理这个异常就可以了。
处理异常的方法是使用 try 和 except 这两个关键字
def convert_number(s):
try:
return int(s)
except ValueError:
return None
你把要试着运行的代码放到 try 的区段里再将出错后要运行的代码放到 except
区段里。在这里我们要试着调用 int() 去处理某个可能是数字的东西如果中间出了错我们就抓到这个错误然后返回 None。
在你写的扫描器里面你应该使用这个函数来测试某个东西是不是数字。做完这个检查你就可以声明这个单词是一个错误单词了。
你应该测试的东西¶
这里是你应该使用的测试文件 tests/lexicon_tests.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46from nose.tools import *
from ex48 import lexicon
def test_directions():
assert_equal(lexicon.scan(north), [(direction, north)])
result lexicon.scan(north south east)
assert_equal(result, [(direction, north),
(direction, south),
(direction, east)])
def test_verbs():
assert_equal(lexicon.scan(go), [(verb, go)])
result lexicon.scan(go kill eat)
assert_equal(result, [(verb, go),
(verb, kill),
(verb, eat)])
def test_stops():
assert_equal(lexicon.scan(the), [(stop, the)])
result lexicon.scan(the in of)
assert_equal(result, [(stop, the),
(stop, in),
(stop, of)])
def test_nouns():
assert_equal(lexicon.scan(bear), [(noun, bear)])
result lexicon.scan(bear princess)
assert_equal(result, [(noun, bear),
(noun, princess)])
def test_numbers():
assert_equal(lexicon.scan(1234), [(number, 1234)])
result lexicon.scan(3 91234)
assert_equal(result, [(number, 3),
(number, 91234)])
def test_errors():
assert_equal(lexicon.scan(ASDFADFASDF), [(error, ASDFADFASDF)])
result lexicon.scan(bear IAS princess)
assert_equal(result, [(noun, bear),
(error, IAS),
(noun, princess)])
记住你要使用你的项目骨架来创建新项目将这个测试用例写下来不许复制粘贴然后编写你的扫描器直至所有的测试都能通过。注意细节并确认结果一切工作良好。
设计的技巧¶
集中一次实现一个测试项目尽量保持项目简单只要把你的 lexicon.py 词汇表中所有的单词放那里就可以了。不要修改输入的单词表不过你需要创建自己的新列表里边包含你的语汇元组。另外记得使用 in 关键字来检查这些语汇列表以确认某个单词是否在你的语汇表中。
加分习题¶
改进单元测试让它覆盖到更多的语汇。
向语汇列表添加更多的语汇并且更新单元测试代码。
让你的扫描器能够识别任意大小写的词汇。更新你的单元测试以确认其功能。
找出另外一种转换为数字的方法。
我的解决方案用了 37 行代码你的是更长还是更短呢