笨方法学Python习题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()

语汇元组

检查句子断成的单词的类型——将用到元组数据结构。元组实际就是不能修改的列表。

first_word = ('direction', 'north')
second_word = ('verb', 'go')
sentence = [first_word, second_word]

这样就创建了一个(TYPE, WORD)组,让你识别出单词,并对它执行指令。

总结:接收用户输入,用split将其分为单词,分析这些单词,识别其类型,最后重新组成一个句子。

扫描输入

写一个扫描器:将用户输入的字符串当做参数,返回有多个(TOKEN, WORD)组成的一个列表,这个列表实现类似句子的功能。若一个单词不在预定的单词语汇表中,那它返回时WORD还在,但TOKEN应该设置成一个专门的错误标记——告诉用户哪里出错了。你需要自己写出来,并通过下面给的单元测试。

异常和数字

数字转换,需要用‘异常’来做,异常指运行某个函数得到的错误。你需要去‘处理’这个异常。

>>> int('hell')
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'hell'

ValueError就是int()函数抛出的一个异常,你只需处理这个异常就行了:

def convert_number(s):
    try:
        return int(s)
    except ValueError:
        return None

把试着运行的代码放到try块里,再将出错后要运行的代码放到except块里。上面代码就是试着调用int()去处理某个可能是数字的东西,若中间出错,就抓到这个错误,然后返回None。

在你写的扫描器中,应使用上面这个函数来测试某个东西是不是数字。做完这个检查,就可以声明这个单词是一个错误单词。

应该测试的东西

测试文件tests/lexicon_tests.py

from 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'), 
                                     ('noun', 'IAS'),
                                     ('noun', 'princess')])

记住要使用你的项目骨架来创建新项目,将此测试用例写下来,然后编写你的扫描器,直至所有的测试都能通过。注意细节并确认一切工作良好。

设计提示

集中一次实现一个测试项目,尽量保持项目简单,只要把你的lexicon.py模块中的语汇表的所有单词放那里就可以了。不要修改输入的单词列表,但要创建自己的新列表,里边包含你的语汇元组。记得使用in关键字来检查这些语汇列表,以确认某个单词是否在你的语汇表中。在你的解决方案中使用目录。

附加练习

1. 改进单元测试,让它覆盖到更多词汇

2. 向语汇列表添加更多的单词,并更新单元测试代码

3. 确认你的扫描器能识别任意大小写的单词。更新单元测试以确认它实际工作

4. 找出另一种转换数字的方法

5. 我的解决方案用了37行代码,你的呢?

常见问题回答

为什么我老看到ImportError?

通常四种错误导致ImportError:在模块路径下没有创建__init__.py;在错误路径下执行了import;拼写错误,导致导入错误模块;没有设置到PYTHONPATH, 所以无法从当前路径加载模块

try-except和if-else有什么不同?

try-except仅用于处理异常,绝不要将其作为if-else使用

有没有办法让游戏在等待用户输入的时候不间断的运行?

我猜你想将游戏做的更高级,用户反应过慢就被杀死之类的。这个可以有,但需要用到更高级模块和编程技巧,本书不涉及。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值