有没有更多的pythonic方法可以编写多个比较

时间:2019-06-11 12:42:23

标签: python

我正在写一个简单的纸牌游戏(类似于Snap)。我已经使其正常运行,没有任何问题,但是我认为应该有一个更优雅的解决方案。 给定一组获胜条件:
Y胜过R
R胜过B
B击败Y

我想比较两张牌,并将两张牌分配给获胜者。 警告:我正在中学阶段教学(无OOP),并希望能够与学生讨论生成的代码。

我想把最后的条件保留为省略号,因为我想返回并在选项列表中添加额外的卡片

if-elif链可以正常工作;我想知道是否有更优雅的解决方案。

    #I have code that randomly selects from a list, but this is the basic 
    #idea:
    p1=input("enter r,y or b")  
    p2=input("enter r,y or b")  

    stack1=[]  
    stack2=[]  

    if   p1=="r" and p2=="b":  
        stack1.extend([p1,p2])  
    elif p1=="y" and p2=="r":  
        stack1.extend([p1,p2])  
    elif p1 =="b" and p2 =="y":  
        stack1.extend([p1,p2])  
    elif p2 =="r" and p1 =="b":  
        stack2.extend([p1,p2])  
    elif p2 =="y" and p1 =="r":  
        stack2.extend([p1,p2])             
    elif p2 =="b" and p1 =="y":  
        stack2.extend([p1,p2])  

    print(stack1)  
    print(stack2)  

我从其余部分中摘录了代码-卡都是随机生成的,因此实际上不需要用户输入。

5 个答案:

答案 0 :(得分:5)

创建一个新字典,其中Y,R,B分别映射到0、1、2。

win_map = {"Y": 0, "R": 1, "B": 2}

我们可以在此处看到循环关系。 0个拍子1个,1个拍子2个和2个拍子0。使用简单的>可以容易地确定前两种情况,但是考虑到第三种情况则需要另一种方法。通过一点点的独创性,我们可以看到我们可以通过加1并使用模运算%来“包装”。 (0+1) % 3 == 1(1+1) % 3 == 2(2+1) % 3 == 0,这3种情况是唯一确定获胜者的情况。

if (win_map[p1] + 1) % 3 == win_map[p2]: ...  # p1 wins
else if (win_map[p2] + 1) % 3 == win_map[p1]: ... # p2 wins

我不确定这将如何传达给学生,但这是一个更干净的解决方案。

注意:此方法不适用于更多卡,因为循环关系将被破坏。

答案 1 :(得分:3)

因此,您的获胜条件看起来像是(获胜者,失败者)对的集合,并且将您的(p1, p2)输入与其进行比较似乎很简单。

win_conditions = {
    ('y', 'r'),
    ('r', 'b'),
    ('b', 'y')
}

p1=input("enter r,y or b")
p2=input("enter r,y or b")

stack1=[]
stack2=[]

if (p1, p2) in win_conditions:
    stack1.extend([p1,p2])
elif (p2, p1) in win_conditions:
    stack2.extend([p1,p2])
else:
    raise ValueError('{} and {} cannot beat each other.'.format(p1, p2))

请注意,如果您假设获胜条件详尽无遗,则可以简化代码。

我认为,如果您向学生展示如何通过在具有专有名称的函数中封装低级操作以提高意图的目的,将使他们受益,那么

def beats(p1, p2):
    return (p1, p2) in win_conditions

if beats(p1, p2):
    stack1.extend([p1,p2])
elif beats(p2, p1):
    stack2.extend([p1,p2])
else:
    raise ValueError('"{}" and "{}" cannot beat each other.'.format(p1, p2))

也许您可以通过扩展列表为要实现的目标找到一个更好的名称。

答案 2 :(得分:1)

针对像您这样的小规模问题的“标准”解决方案是将所有可能性都放入地图中:

result_map = { ('r', 'b'): 1, ('b', 'y'): 1, ('y', 'r'): 1, 
('b', 'r'): 2, ('y', 'b'): 2, ('r', 'y'): 2 }
v = result_map.get((p1, p2), None)
if v == 1:
    stack1.extend([p1, p2])
elif v == 2:
    stack2.extend([p1, p2])

为什么这样?因为它为您提供了一种简单的方法来更改获胜/失败条件(只需更改字典),所以获胜/失败规则可以完全任意,并且可以轻松地遵循代码(图像中出现了一些怪异的if-else条件,而其他人正在寻找对此代码感到好奇,这是怎么回事,为什么)。

答案 3 :(得分:0)

您重复同一件事的次数太多,有两个extend呼叫,每个呼叫重复3次。

您可以使用or关键字来大大简化代码:

# extend to "stack1"
if (p1 == "r" and p2 == "b") or (p1 == "y" and p2 == "r") or (p1 == "b" and p2 == "y"):
  stack1.extend([p1, p2])

# extend to "stack2"
elif (p2 == "r" and p1 == "b") or (p2 == "y" and p1 == "r") or (p2 == "b" and p1 == "y"):
  stack2.extend([p1, p2])

祝你好运。

答案 4 :(得分:0)

这只是比较的另一种方式,也许可以在for循环或字典映射中使用它,以在必要且性能不重要的情况下帮助重构代码。

from operator import and_ 
from functools import partial

def compare(a, b, c, d):
    return and_(a == c, b == d) 

p1 = 'r'
p2 = 'b'
cmp_p1_p2 = partial(compare, p1, p2)
cmp_p2_p1 = partial(compare, p2, p1)

cmp_p1_p2('r', 'b')
# True
cmp_p2_p1('b', 'r')
# True