一、问题描述
- 一条街上有五座不同颜色的房子,每座房子住着不同国籍的人,每个人抽不同的烟,喝不同的饮料,养不同的宠物。
- 英国人住在红房子里。
- 西班牙人养狗。
- 住在绿房子里的人喝咖啡。
- 乌克兰人喝茶。
- 绿房子就在乳白色房子的右边。
- 抽流金岁月(烟名)的人养蜗牛。
- 抽薄荷烟的住在黄房子里。
- 住在中间的房子里的人喝牛奶。
- 挪威人住在第一座房子里。
- 抽契斯特菲尔德(烟名)的人住在养狐狸的人旁边。
- 抽薄荷烟的人住在养马的人旁边。
- 抽好彩(烟名)的人喝橙汁。
- 日本人抽百乐门(烟名)。
- 挪威人住在蓝房子隔壁。
那么,谁喝水(water)?谁养斑马(zebra)?
二、解决问题思路
首先把问题所涉及到的概念列举出来:house和properties。那么properties包含五个属性分别是:房子的颜色,人的国籍,烟,饮料和宠物
1.如果仅仅为house分配一个properties属性,例如颜色,那么共有**5!**种可能性;若是把这五个属性排列组合分配到五个房子中,那么共有5!5(约等于20billion)种可能性。我们都知道,计算机每秒钟可以处理1billion条指令
2.houses = [1,2,3,4,5] 这是其中一种可能
orderings= F(houses)
for (red,green,ivory,yellow,blue) in orderings:
这里的F应该是排列函数,在itertools模块里面有permutations函数,那么len(orderings)=120
import itertools
def imright(h1, h2):
# "House h1 is immediately right of h2 if h1-h2 == 1."
return h1-h2 == 1
def nextto(h1, h2):
# "Two houses are next to each other if they differ by 1."
return abs(h1-h2) == 1
def zebra_pazzle():
houses = first, _, middle, _, _ = [1, 2, 3, 4, 5]
orderings = list(itertools.permutations(houses)) # 1 constrains
return next((WATER, ZEBRA)
for (red, green, ivory, yellow, blue) in orderings
if imright(green, ivory) #6
for (Englishman, Spaniard, UKranian, Japanese, Norwegian) in orderings
if Englishman is red # 2
if Norwegian is first # 10
if nextto(Norwegian, blue) #15
for (coffee, tea, milk, oj, WATER) in orderings
if coffee is green # 4
if UKranian is tea # 5
if milk is middle # 9
for (OldGold, Kools, Chesterfields, LuckyStrike, Parliaments) in orderings
if Kools is yellow # 8
if LuckyStrike is oj # 13
if Japanese is Parliaments # 14
for (dog, snails, fox, horse, ZEBRA) in orderings
if Spaniard is dog # 3
if OldGold is snails # 7
if nextto(Chesterfields, fox) # 11
if nextto(Kools, horse) # 12
)
print(zebra_pazzle())
>>>(1, 5)
#1-15分别是问题中的15个限制条件。调整顺序可以消除冗余,可以在一秒钟之内得到答案。所以最终结果是:第一个房子喝水,第五个房子拥有zebra
关于生成器(generator)
- 最直观的:generator 用小括号表示( ),而[ ]则表示list
- 一个例子:
def sq(x):
print('sq called', x)
return x*x
g = (sq(x) for x in range(10) if x%2 == 0)
g是一个generator expression,调用next(),那么将会依次输出g里面的东西,直到g为空。所以每取出一个,g里面就会少一个元素,当g为空时在调用next(),将会报错
print(g)
>>><generator object <genexpr> at 0x000001561771DCA8>
next(g)
>>>sq called 0
next(g)
>>>sq called 2
next(g)
>>>sq called 4
next(g)
>>>sq called 6
next(g)
>>>sq called 8
next(g)
>>>Traceback (most recent call last):
File "E:/untitled1/prac.py", line 50, in <module>
next(g)
StopIteration
故为了避免编译器报错,我们可以这样做
for x2 in (sq(x) for x in range(10) if x%2 == 0): pass
>>>sq called 0
>>>sq called 2
>>>sq called 4
>>>sq called 6
>>>sq called 8
为什么选用生成器来解决问题
- less indentation
因为有太多的for和if语句,那么将会造成许多缩进,可能页面的空间会不够用 - stop early
- easier to edit
听这一节课自己不熟悉的小概念
- *arg----*arg出现在两个地方:
一个是定义函数的形参;这意味着该函数可以带任何数量的参数,他们应该在一个名叫arg的tuple中;
一个是函数调用的时候的参数 :调用的时候unpack该tuple;
下面是一个小例子:
第一个地方:def something(fn,*arg):
第二个地方:fn(*arg)
若something(fn,(1,2,3))
那么调用的时候就是fn(1,2,3)
其实这仅仅是pack和unpack参数(argument)的一种方式
- 对于列表里面嵌套for语句,始终有些迷糊。下面是我的另外一篇介绍该内容的博客,请参考:
https://blog.csdn.net/weixin_43294226/article/details/88902779 - 求函数的运行时间的函数,这里面应用到了*arg和list嵌套for语句:
import time
def timedcall(fn, *args):
"Call function with args; return the time in seconds and result."
t0 = time.clock() # clock()求当时的时间
result = fn(*args)
t1 = time.clock()
return t1-t0, result
def average(numbers):
"Return the average (arithmetic mean) of a sequence of numbers."
return sum(numbers) / float(len(numbers))
def timedcalls(n, fn, *args):
"""Call fn(*args) repeatedly: n times if n is an int, or up to
n seconds if n is a float; return the min, avg, and max time"""
# Your code here.
if isintance(n,int):
times = [timedcall(fn, *args)[0] for _ in range(n)]
else:
times = []
while sum(times) < n:
times.append(timedcall(fn,*args)[0])
return min(times), average(times), max(times)
generator function
这里接着讲到了generator function,如何应用在上面的问题中我就不介绍了,因为他比起上面给出的问题解决方法稍微差了那么一点,但是generator function是什么我们还是要介绍一下。
def ints(start, end = None): # end本可以不等于None,是一个正常的数字就可以,但是它一旦等于None了,就表明这个函数要无休止的进行下去了
i = start
while i <= end or end is None:
yield i
i = i + 1
def all_ints():
"Generate integers in the order 0, +1, -1, +2, -2, +3, -3, ..."
# Your code here.
yield 0
for i in ints(1):
yield +i
yield -i
这两个函数都含有yield,所以他们都是generator function,这是使函数区别于普通函数的根本。
aspect-oriented program
aspect-oriented program 遵循以下三个aspect:
correct
efficient
debug
结语
zebra_puzzle 问题就告一段落,如有不好的地方,还请朋友们指正,感谢!