《Design of Computer Programs》学习笔记(2 - 2)Back of the Envelope - Problem Set 2

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)))
lettersformula字符串中的所有不重复的大写字母组成的字符串。例如,对于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

如果verboseTrue,那么打印出f所代表的lambda表达式的字符串;
如果verboseFalse,则不打印。
不管verboseTrueFalse,都将使用eval(f)求出f所代表的lambda表达式为True或者False;并且,都将返回eval(f), letters。其中,eval(f)True或者Falselettersformula字符串中的所有不重复的大写字母组成的字符串。例如,对于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)

参考文献:

  1. Design of Computer Programs - 英文介绍 - Udacity
  2. Design of Computer Programs - 中文介绍 - 果壳
  3. Design of Computer Programs - 视频列表 - Udacity
  4. Design of Computer Programs - 视频列表 - YouTube
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值