Back of the Envelope
Problem Set 2
视频链接:
Problem Set 2 - Udacity
1. 练习:No Leading Zeros
No Leading Zeros - Design of Computer Programs - YouTube
补充视频中,一些不认识的词汇
本段视频的核心部分的,我的翻译
# --------------
# User Instructions
#
# Modify the function compile_formula so that the function
# it returns, f, does not allow numbers where the first digit
# is zero. So if the formula contained YOU, f would return
# False anytime that Y was 0
import re
import itertools
import string
def compile_formula(formula, verbose=False):
"""Compile formula into a function. Also return letters found, as a str,
in same order as parms of function. The first digit of a multi-digit
number can't be 0. So if YOU is a word in the formula, and the function
is called with Y eqal to 0, the function should return False."""
# modify the code in this function.
letters = ''.join(set(re.findall('[A-Z]', formula)))
parms = ', '.join(letters)
tokens = map(compile_word, re.split('([A-Z]+)', formula))
body = ''.join(tokens)
f = 'lambda %s: %s' % (parms, body)
if verbose: print f
return eval(f), letters
def compile_word(word):
"""Compile a word of uppercase letters as numeric digits.
E.g., compile_word('YOU') => '(1*U+10*O+100*Y)'
Non-uppercase words uncahanged: compile_word('+') => '+'"""
if word.isupper():
terms = [('%s*%s' % (10**i, d))
for (i, d) in enumerate(word[::-1])]
return '(' + '+'.join(terms) + ')'
else:
return word
def faster_solve(formula):
"""Given a formula like 'ODD + ODD == EVEN', fill in digits to solve it.
Input formula is a string; output is a digit-filled-in string or None.
This version precompiles the formula; only one eval per formula."""
f, letters = compile_formula(formula)
for digits in itertools.permutations((1,2,3,4,5,6,7,8,9,0), len(letters)):
try:
if f(*digits) is True:
table = string.maketrans(letters, ''.join(map(str, digits)))
return formula.translate(table)
except ArithmeticError:
pass
def test():
assert faster_solve('A + B == BA') == None # should NOT return '1 + 0 == 01'
assert faster_solve('YOU == ME**2') == ('289 == 17**2' or '576 == 24**2' or '841 == 29**2')
assert faster_solve('X / X == X') == '1 / 1 == 1'
return 'tests pass'
test()
注意到compile_formula(formula, verbose=False)
中的说明
The first digit of a multi-digit number can't be 0. So if YOU is a word in the formula, and the function is called with Y eqal to 0, the function should return False.
如果'YOU'
是formula
中的1个单词,并且,调用compile_formula
函数时,Y
等于0
,那么,compile_formula
函数应该返回False
。
我认为,要修改compile_formula
函数,达到如上所述的目的,需要充分理解所有函数的作用。
先看看compile_formula
函数。这个函数中,调用了compile_word(word)
函数。我们先来看看该函数的作用。以一段代码来演示:
def compile_word(word):
"""Compile a word of uppercase letters as numeric digits.
E.g., compile_word('YOU') => '(1*U+10*O+100*Y)'
Non-uppercase words uncahanged: compile_word('+') => '+'"""
if word.isupper():
terms = [('%s*%s' % (10**i, d))
for (i, d) in enumerate(word[::-1])]
return '(' + '+'.join(terms) + ')'
else:
return word
print compile_word('YOU') # (1*U+10*O+100*Y)
print compile_word('+') # +
compile_word(word)
函数的作用,清晰明了:如果输入word不是大写字母,则原样返回;如果是大写字母,则将该word的每1个字母的每1位看做个、十、百、千……位,分别与1、10、100、1000……相乘,返回字符串。例如,compile_word('YOU')
返回字符串'(1*U+10*O+100*Y)'
。
再来看看compile_formula()
。
def compile_formula(formula, verbose=False):
# modify the code in this function.
letters = ''.join(set(re.findall('[A-Z]', formula)))
parms = ', '.join(letters)
tokens = map(compile_word, re.split('([A-Z]+)', formula))
body = ''.join(tokens)
f = 'lambda %s: %s' % (parms, body)
if verbose: print f
return eval(f), letters
letters = ''.join(set(re.findall('[A-Z]', formula)))
letters
是formula
字符串中的所有不重复的大写字母组成的字符串。例如,对于formula = 'I + I = ME'
,letters
等于'IEM'
。补充知识:re.findall(pattern, text)。
parms = ', '.join(letters)
parms
是将letters字符串中的每个字符,用', '
连接,后面将用到lambda 左边:右边
的冒号的左边,作为lambda
表达式的参数使用。例如,对于letters = 'IEM'
,parms
等于'I, E, M'
。补充知识:string.join()。
tokens = map(compile_word, re.split('([A-Z]+)', formula))
re.split('([A-Z]+)', formula)
的意思是,将字符串formula分解,分解的规则是,将1个或多个大写字母作为分隔符。例如,对于formula = 'YOU==ME**2'
,re.split('([A-Z]+)', formula)
等于['', 'YOU', '==', 'ME', '**2']
。补充知识:re.split()。
tokens = map(compile_word, re.split('([A-Z]+)', formula))
,是将compile_word
函数,作用在re.split('([A-Z]+)', formula)
返回的字符串上。实际的结果是,tokens
存放了若干个字符串的1个列表,这些字符串分别是formula
字符串所代表的等式的运算符号(如加减乘除)、字母与对应位置的10的倍数的乘积。例如,对于formula='I + I == ME'
有tokens
等于['', '(1*I)', ' + ', '(1*I)', ' == ', '(1*E+10*M)', '']
。
body = ''.join(tokens)
这行代码是将tokens
连接为1个等式的字符串,后面将作为lambda 左边:右边
的右边的表达式。例如:对于tokens = ['', '(1*I)', ' + ', '(1*I)', ' == ', '(1*E+10*M)', '']
,body
等于'(1*I) + (1*I) == (1*E+10*M)'
。
f = 'lambda %s: %s' % (parms, body)
总的来说,这行代码是将最初的formula
字符串,转换为1个lambda
表达式的字符串。例如:对于字符串formula = 'I + I == ME'
,有parms = 'I, E, M'
、body = '(1*I) + (1*I) == (1*E+10*M)'
,f
等于'lambda I, E, M: (1*I) + (1*I) == (1*E+10*M)'
。
if verbose: print f
return eval(f), letters
如果verbose
为True
,那么打印出f
所代表的lambda
表达式的字符串;
如果verbose
为False
,则不打印。
不管verbose
为True
或False
,都将使用eval(f)
求出f
所代表的lambda
表达式为True
或者False
;并且,都将返回eval(f), letters
。其中,eval(f)
为True
或者False
,letters
为formula
字符串中的所有不重复的大写字母组成的字符串。例如,对于formula = 'I + I = ME'
,letters
等于'IEM'
。
先暂时谈下我的思路吧。可以先找出formula
中,长度大于1的所有单词words,迭代遍历其中的每1个单词word,对于单词word的首字母word[0]
对应的数字值value,如果value是0,则返回False
。于是,有了如下的代码:
def compile_formula(formula, verbose=False):
letters = ''.join(set(re.findall('[A-Z]', formula)))
parms = ', '.join(letters)
tokens = map(compile_word, re.split('([A-Z]+)', formula))
body = ''.join(tokens)
f = 'lambda %s: %s' % (parms, body)
if verbose: print f
words = re.findall('[A-Z]+', formula)
for word in words:
if len(word) > 1:
if eval(word[0] + '==' + '0') == True:
return False
else:
continue
return eval(f), letters
words = re.findall('[A-Z]+', formula)
这行代码的含义是,找出formula
中的所有的1个、2个、3个……大写字母组成的字符串的1个列表words
。例如,对于formula = 'I + I == ME'
,words
等于['I', 'I', 'ME']
。补充知识:re.findall(pattern, text) - 第5个例子。
但是,如上代码,测试不通过:
Traceback (most recent call last):
File "y.py", line 69, in <module>
test()
File "y.py", line 64, in test
print faster_solve('A + B == BA')
File "y.py", line 53, in faster_solve
f, letters = compile_formula(formula)
File "y.py", line 32, in compile_formula
if eval(word[0] + '==' + '0') == True:
File "<string>", line 1, in <module>
NameError: name 'B' is not defined
Peter的答案:
def compile_formula(formula, verbose=False):
"""Compile formula into a function. Also return letters found, as a str,
in same order as parms of function. The first digit of a multi-digit
number can't be 0. So if YOU is a word in the formula, and the function
is called with Y eqal to 0, the function should return False.
For example, 'YOU == ME**2' =>
lambda Y, M, E, U, O: Y!=0 and M!=0 and ((1*U+10*O+100*Y)) == (1*E+10*M)**2"""
letters = ''.join(set(re.findall('[A-Z]', formula)))
firstletters = set(re.findall(r'\b([A-Z])[A-Z]', formula))
parms = ', '.join(letters)
tokens = map(compile_word, re.split('([A-Z]+)', formula))
body = ''.join(tokens)
if firstletters:
tests = ' and '.join(L+'!=0' for L in firstletters)
body = '%s and (%s)' % (parms, body)
f = 'lambda %s: %s' % (parms, body)
if verbose: print f
return eval(f), letters
firstletters = set(re.findall(r'\b([A-Z])[A-Z]', formula))
对于'\b([A-Z])[A-Z]'
:'\b'
表示,1个单词开头或末尾的空串,指定输入文本中模式应当出现的相对位置;第1处的([A-Z])
表示,查找第1个位置处的大写字母;第2处的[A-Z]
表示,应当还要有另外的大写字母。
所以,re.findall(r'\b([A-Z])[A-Z]', formula)
的意思是,找到formula
字符串中,所有的2个或者多于2个大写字母的单词(当然,也可能不是单词,仅仅是多个大写字母的机械连接)的首字母。
firstletters = set(re.findall(r'\b([A-Z])[A-Z]', formula))
则是对re.findall(r'\b([A-Z])[A-Z]', formula)
中,可能存在的重复的字母,进行去重,并且返回1个集合给firstletters
。
辅助对firstletters = set(re.findall(r'\b([A-Z])[A-Z]', formula))
的理解,参见下面的例子:
>>> import re
>>> formula = 'YOU == ME**2'
>>> re.findall(r'\b([A-Z])[A-Z]', formula)
['Y', 'M']
>>> firstletters = set(re.findall(r'\b([A-Z])[A-Z]', formula))
>>> firstletters
set(['Y', 'M'])
上述代码中,这一段:
if firstletters:
tests = ' and '.join(L+'!=0' for L in firstletters)
body = '%s and (%s)' % (parms, body)
作用是,将formula
中出现的连续2个或者2个以上的大写字母的首字母,进行非0的判断,并且,整合进lambda
表达式。
2. 练习:Floor Puzzle
Floor Puzzle - Design of Computer Programs - YouTube
补充视频中,一些不认识的词汇
resident n.居民
inhabitant 居民,住户
denoting 指示;指出
adjacent 领近的;相邻
本段视频的核心部分的,我的翻译
#------------------
# User Instructions
#
# Hopper, Kay, Liskov, Perlis, and Ritchie live on
# different floors of a five-floor apartment building.
#
# Hopper does not live on the top floor.
# Kay does not live on the bottom floor.
# Liskov does not live on either the top or the bottom floor.
# Perlis lives on a higher floor than does Kay.
# Ritchie does not live on a floor adjacent to Liskov's.
# Liskov does not live on a floor adjacent to Kay's.
#
# Where does everyone live?
#
# Write a function floor_puzzle() that returns a list of
# five floor numbers denoting the floor of Hopper, Kay,
# Liskov, Perlis, and Ritchie.
import itertools
def floor_puzzle():
# Your code here
return [Hopper, Kay, Liskov, Perlis, Ritchie]
先来看看我的两个解法:
import itertools
def floor_puzzle():
floors = [1, 2, 3, 4, 5]
t = itertools.permutations(floors)
for Hopper, Kay, Liskov, Perlis, Ritchie in t:
if Hopper != 5:
if Kay != 1:
if Liskov !=5 and Liskov != 1:
if Perlis - Kay == 1:
if abs(Ritchie - Liskov) > 1:
if abs(Liskov - Kay) >1:
return [Hopper, Kay, Liskov, Perlis, Ritchie]
print floor_puzzle()
import itertools
def floor_puzzle():
floors = bottom, _, _, _, top = [1, 2, 3, 4, 5]
p = itertools.permutations(floors)
Hopper, Kay, Liskov, Perlis, Ritchie = 0, 0, 0, 0, 0
return ([Hopper, Kay, Liskov, Perlis, Ritchie]
for Hopper, Kay, Liskov, Perlis, Ritchie in p
if Hopper is not top
if Kay is not bottom
if Liskov is not top
if Liskov is not bottom
if Perlis - Kay == 1
if Ritchie - Liskov > 1
if Liskov -Kay > 1
)
for i in floor_puzzle():
print i
但是,我的解法均测试不通过,来看看助教的解法:
import itertools
def floor_puzzle():
floors = bottom, _, _, _, top = [1, 2, 3, 4, 5]
orderings = list(itertools.permutations(floors))
for (Hopper, Kay, Liskov, Perlis, Ritchie) in orderings:
if (Hopper is not top
and Kay is not bottom
and Liskov is not top
and Liskov is not bottom
and Perlis > Kay
and abs(Ritchie - Liskov) > 1
and abs(Liskov - Kay) > 1
):
return [Hopper, Kay, Liskov, Perlis, Ritchie]
print floor_puzzle() # [3, 2, 4, 5, 1]
3. 练习:Subpalindrome
Subpalindrome - Design of Computer Programs - YouTube
补充视频中,一些不认识的词汇
palindrome 回文
space 空白;空格
punctuation 标点法;标点符号
comparison 比较,对照;比较级
routine 例行程序
trick 戏法,把戏;计谋,诀窍;骗局;恶作剧
expand vt.使…变大;扩张;详述
indices index的复数
convention 惯例,习俗,规矩
abbreviation 省略,缩写,简化,缩写词,略语
assign 分配;赋值
recurrence 反复,隐现;循环;重新提起
eliminate排除;消除
potential 潜在的,有可能的
本段视频的核心部分的,我的翻译
# --------------
# User Instructions
#
# Write a function, longest_subpalindrome_slice(text) that takes
# a string as input and returns the i and j indices that
# correspond to the beginning and end indices of the longest
# palindrome in the string.
#
# Grading Notes:
#
# You will only be marked correct if your function runs
# efficiently enough. We will be measuring efficency by counting
# the number of times you access each string. That count must be
# below a certain threshold to be marked correct.
#
# Please do not use regular expressions to solve this quiz!
def longest_subpalindrome_slice(text):
"Return (i, j) such that text[i:j] is the longest palindrome in text."
# Your code here
def test():
L = longest_subpalindrome_slice
assert L('racecar') == (0, 7)
assert L('Racecar') == (0, 7)
assert L('RacecarX') == (0, 7)
assert L('Race carr') == (7, 9)
assert L('') == (0, 0)
assert L('something rac e car going') == (8,21)
assert L('xxxxx') == (0, 5)
assert L('Mad am I ma dam.') == (0, 15)
return 'tests pass'
print test()
我的代码:
def h2(text):
text = text.lower()
for i in range(len(text)):
for j in range(i+1, len(text)-i-1):
if text[i:j] != text[i:j:-1]:
continue
else:
return (i, j)
print h2('racecar')
print h2('Racecar')
print h2('Racecarr')
print h2('Race carr')
print h2('something rac e car going')
print h2('xxxxx')
print h2('Mad am I ma dam.')
我的代码,基于这样的想法:检测1个长字符串中,从左到右的每1个子字符串,是否为回文字符串。例如,对于'racecar'
,检测r、ra、rac、race……,第2轮检测a、ac、ace、acec等等,直至检测完所有可能的子字符串。
但是我的代码,测试不通过。
Peter的代码:
def longest_subpalindrome_slice(text):
"Return (i,j) such that text[i:j] is the longest palindrome in text."
if text == '': return (0,0)
def length(slice): a,b = slice; return b-a
candidates = [grow(text, start, end)
for start in range(len(text))
for end in (start, start+1)]
return max(candidates, key=length)
def grow(text, start, end):
"Start with a 0- or 1- length palindrome; try to grow a bigger one."
while (start > 0 and end < len(text)
and text[start-1].upper() == text[end].upper()):
start -= 1; end += 1
return (start, end)