问题:
我已经看到了一些关于man子手的问题。通常,这是通过非常骇人听闻的方式完成的,通常无法进一步推广。我的想法或问题是关于礼服的创建,这是任何子手游戏的核心部分。
我想在下面创建ASCII图像
 _____
 |   |
 O   |
/|\  |
/ \  |
     |          
 ---------- 

每次提示该功能时,棒形图应出现。
我还希望能够指定礼服的高度和宽度。但是,我无法相对于礼服尺寸来缩放简笔画。下面提供了代码,对此我有两个简单的问题=)
问题:

是否有更干净的方法来吊人?我觉得我的方法很野蛮(我使用了一些黑色voodo研究生数学来使它看起来正确)。 (本质上是提供更多的猜测)。

代码:
from math import ceil


def create_gown(width, height):
    gown = []
    gown.append('{:>{}s}'.format('_'*int(width/2), 10))
    gown.append('{:>{}s} {:>{}s}'.format(
        '|', 11 - int(width/2), '|', int(width/2)-2))
    for i in range(height-3):
        gown.append('{:^{}s}'.format('|', 20))
    gown.append('{:^{}s}'.format('-'*width, 20))
    return gown


def wrong_answer(gown, attempt=0):

    height, width = len(gown), len(gown[-1].strip())

    offset1 = int((-width+23)*0.5)+1
    offset3 = int(ceil(0.5*(width-7)))
    if attempt == 0:
        return gown
    elif attempt == 1:
        new_line = '{:>{}s} {:>{}s}'.format('O', offset1-1, '|', offset3+1)
        row = 2
    elif attempt == 2:
        new_line = '{:>{}s} {:>{}s}'.format('|', offset1-1, '|', offset3+1)
        row = 3
    elif attempt == 3:
        new_line = '{:>{}s} {:>{}s}'.format('/| ', offset1, '|', offset3)
        row = 3
    elif attempt == 4:
        new_line = '{:>{}s} {:>{}s}'.format('/|\', offset1, '|', offset3)
        row = 3
    elif attempt == 5:
        new_line = '{:>{}s} {:>{}s}'.format('/  ', offset1, '|', offset3)
        row = 4
    elif attempt == 6:
        new_line = '{:>{}s} {:>{}s}'.format('/ \', offset1, '|', offset3)
        row = 4
    else:
        raise Exception("Ops! The number of attempts must be an integer from 0 to 6.")
    gown[row] = new_line
    return gown


def print_gown(gown):
    for line in gown:
        print line

if __name__ == '__main__':
    gown = create_gown(10, 7)
    print len(gown)
    for i in range(7):
        gown = wrong_answer(gown, i)
        print_gown(gown)


评论

您说“礼服”时,您是说“绞架”吗?

吊死男人最常见但不一定正确的方法是吊死他的正确方法;)

我回滚了最后的编辑,因为它使Mathias的答案无效。请查看当有人回答时该怎么办。

该问题已被选为“ 2016年最佳代码评论”(Best of Code Review)“最佳标题”的得主。

oo!

#1 楼

重复

所有的elif都具有相同的结构。您只需要确保插入的花样始终为3宽,就可以放下偏移调整。合理的下一步是使用字典来存储每次尝试的模式和行:

_GOWN_MODIFIER = {
    1: (' O ', 2),
    2: (' | ', 3),
    3: ('/| ', 3),
    4: ('/|\', 3),
    5: ('/  ', 4),
    6: ('/ \', 4),
}

def wrong_answer(gown, attempt=0):

    height, width = len(gown), len(gown[-1].strip())

    offset1 = int((-width+23)*0.5)+1
    offset3 = int(ceil(0.5*(width-7)))
    if not attempt:
        return gown

    try:
        pattern, row = _GOWN_MODIFIER[attempt]
    except KeyError:
        raise Exception("Ops! The number of attempts must be an integer from 0 to 6.")
    else:
        gown[row] = '{:>{}s} {:>{}s}'.format(pattern, offset1, '|', offset3)

    return gown


微积分

您不需要ceilint也不能执行2的整数除法。在Python 2中,/已经是您需要的运算符。您也可以使用//,这也是相同的操作。后者优于前者的优势在于,Python 3中的行为相同,而/将在Python 3中执行小数除法。


您知道可以使用seq(element) * count创建重复元素的序列。您可以通过重复极点,然后修改顶部的两行和最后一行来创建gown变量。它应该更便于存储,因为存储列表所需的空间将一口气分配:

_GOWN_MODIFIER = {
    1: (' O ', 2),
    2: (' | ', 3),
    3: ('/| ', 3),
    4: ('/|\', 3),
    5: ('/  ', 4),
    6: ('/ \', 4),
}

def wrong_answer(gown, attempt=0):
    if not attempt:
        return gown

    height, width = len(gown), len(gown[-1].strip())
    offset1 = (23 - width) // 2 + 1
    offset3 = (width - 6) // 2

    try:
        pattern, row = _GOWN_MODIFIER[attempt]
    except KeyError:
        raise Exception("Ops! The number of attempts must be an integer from 0 to 6.")
    else:
        gown[row] = '{:>{}s} {:>{}s}'.format(pattern, offset1, '|', offset3)

    return gown

def create_gown(width, height):
    half_width = width // 2
    gown = []
    gown.append('{:>{}s}'.format('_'*half_width, 10))
    gown.append('{:>{}s} {:>{}s}'.format(
        '|', 11 - half_width, '|', half_width-2))
    for i in range(height-3):
        gown.append('{:^{}s}'.format('|', 20))
    gown.append('{:^{}s}'.format('-'*width, 20))
    return gown


就地修改并返回值时

还有一点关于打印的信息

您的wrong_answer都修改了礼服并返回修改后的值。这是不必要的,因为调用方仍应保留对该值的引用(已被修改)。调用代码时,您可以拥有:复制对同一对象的引用。相反,您可以做的是预处理打印作业,因此您实际上不需要print_gown函数:

def create_gown(width, height):
    half_width = width // 2
    gown = ['{:^{}s}'.format('|', 20)] * height
    gown[0] = '{:>{}s}'.format('_' * half_width, 10)
    gown[1] = '{:>{}s} {:>{}s}'.format(
        '|', 11 - half_width, '|', half_width-2)
    gown[-1] = '{:^{}s}'.format('-' * width, 20)
    return gown


用法:

gown2 = wrong_answer(gown, attempt=3)


异常

提高通用Exception是不好的做法,因为它使试图处理您的代码的except子句能够捕获比应有的更多的代码。您至少应该使用更通用的异常(例如ValueError)或定义自己的异常:调用者应通过增加wrong_answer来调用此函数。这是发电机的工作。通过将attempts变成生成器,可以调用它来创建生成器实例,然后在此实例上调用wrong_answer(或让next循环为您完成)以获取下一件要打印的礼服:
>>> gown2 is gown
True


用法:

def wrong_answer(gown, attempt=0):
    height, width = len(gown), len(gown[-1].strip())
    offset1 = (23 - width) // 2 + 1
    offset3 = (width - 6) // 2

    if attempt:
        try:
            pattern, row = _GOWN_MODIFIER[attempt]
        except KeyError:
            raise Exception("Ops! The number of attempts must be an integer from 0 to 6.")
        else:
            gown[row] = '{:>{}s} {:>{}s}'.format(pattern, offset1, '|', offset3)

    return '\n'.join(gown)


,或者在更实际的情况下:

print(wrong_answer(gown, attempt=3))


魔术数字

for似乎将礼服放在20个大小的字符串中居中,但是,如果create_gown大于该值怎么办?首先,您应该定义此值并为其赋予一个有意义的名称,并且所有衍生自该值的值都应相同。然后,您可能希望以width作为微积分的基础。

评论


\ $ \ begingroup \ $
可爱的答案!您是否确定`try:pattern,row = _GOWN_MODIFIER [attempt]`部分?似乎捕获了所有错误,而不仅仅是数字不在范围内。关于您对魔术数字的评论,我完全同意。我似乎将礼服摆在20根大小的琴弦中。有办法避免这种情况吗?我试图以高度和宽度为基础,但是遇到了一些问题。如果您有避免微积分的任何技巧,那就太好了(因为尚不清楚它的作用,并且其中包含幻数)。再次在其他部分上提供了很好的建议!
\ $ \ endgroup \ $
– N3buchadnezzar
16年6月7日在13:33



\ $ \ begingroup \ $
try部分与一个KeyError除外配对,因此只有未在_GOWN_MODIFIER词典中定义的尝试才会触发您的自定义异常。仅当尝试运行没有问题时,才会执行else部分。
\ $ \ endgroup \ $
–301_Moved_Permanently
16年6月7日在13:39

\ $ \ begingroup \ $
谢谢!我也完全同意发电机。我尝试将其制作为发电机,但未能做到。感谢您向我展示正确的方法
\ $ \ endgroup \ $
– N3buchadnezzar
16年6月7日在13:41

\ $ \ begingroup \ $
对于宽度部分,应尽量不要将'|'居中在您的中间字符串中。类似于''* half_width +'|'应该会给你杆子和图案+''*(half_width-3)+'|'对于中间零件就足够了。使用格式也可以使其更清洁。
\ $ \ endgroup \ $
–301_Moved_Permanently
16年6月7日在13:44