简体   繁体   English

是否有更多pythonic方式来编写多个比较

[英]Is there a more pythonic way to write multiple comparisons

I am writing a simple card game (Similar to Snap). 我正在写一个简单的纸牌游戏(类似于Snap)。 I've got it working, without problems, but I feel that there ought to be a more elegant solution. 我已经让它工作,没有问题,但我觉得应该有一个更优雅的解决方案。 Given a set of win conditions: 给出一组胜利条件:
Y beats R Y击败R.
R beats B R节拍B.
B beats Y B击败Y.
etc 等等

I want to compare the two player's cards and assign both cards to the winner. 我想比较两个玩家的牌并将两张牌分配给获胜者。 Caveat: I'm teaching at secondary school level (no OOP) and want to be able to discuss the resulting code with students. 警告:我在中学教学(没有OOP),并希望能够与学生讨论最终的代码。

I've left the final condition as an elif, as I wanted to go back and add extra cards to the list of options 我已经离开了最后一个条件作为elif,因为我想回去并在选项列表中添加额外的卡片

The if - elif chain works without problems; if - elif链没有问题; I was wondering if there was a more elegant solution. 我想知道是否有更优雅的解决方案。

    #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)  

I've excerpted the code from the remainder - the cards are all randomly generated, so no user input is actually required. 我从其余部分摘录了代码 - 这些卡都是随机生成的,因此实际上不需要用户输入。

Create a new dictionary with Y, R, B each mapping to 0, 1, 2. 使用Y,R,B创建一个新的字典,每个字典映射到0,1,2。

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

We can see a cyclic relationship here. 我们可以在这看到一个循环关系。 0 beats 1, 1 beats 2, and 2 beats 0. The first two cases are easy to determine using a simple > , but taking the third case into account needs another method. 0节拍1,1节拍2和节拍0.前两种情况很容易使用简单的>确定,但考虑第三种情况需要另一种方法。 With a bit of ingenuity, we can see that we can "wrap" by adding 1 and using a modulo operation % . 通过一点巧思,我们可以看到我们可以通过添加1并使用模运算%来“换行”。 (0+1) % 3 == 1 , (1+1) % 3 == 2 , and (2+1) % 3 == 0 , and these 3 cases are the only cases where a winner is determined. (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

I am not sure how well this will be conveyed to students though, but it is a cleaner solution. 我不确定这会如何传达给学生,但这是一个更清洁的解决方案。

Note: this method will not work with more cards, as the cyclic relationship will be broken. 注意:此方法不适用于更多卡,因为循环关系将被破坏。

So your win conditions look like a collection of (winner, loser) pairs and comparing your (p1, p2) input to them looks like the simplest thing to do. 因此,您的胜利条件看起来像(赢家,输家)对的集合,并将您的(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))

Note that the code can be simplified if you assume that the win conditions are exhaustive. 请注意,如果您认为胜利条件是详尽的,则可以简化代码。

I think you will do your students a favor if you show them how to improve readability by encansulating low-level operations in functions with proper names so that the intent is more obvious. 如果你向他们展示如何通过在具有正确名称的函数中强化低级操作来提高他们的可读性,我认为你会帮助你的学生,这样他们的意图就更明显了。

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))

Maybe you can find a better name for whatever you want to achieve by extending the list. 也许你可以通过扩展列表为你想要的任何东西找到一个更好的名字。

"Standard" solution for small scale problem like yours is to put all possibilities into map: 像您这样的小规模问题的“标准”解决方案是将所有可能性放入地图:

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])

Why like this? 为什么这样? Because it gives you easy way to change win / loose condition (just change a dictionary), win / loose rules can be completely arbitrary and it is easy to follow code (image you've some weird if-else condition and someone else comes looking at this code and wonders, what is going on and why). 因为它为你提供了简单的方法来改变胜利/宽松状态(只是改变一个字典),胜利/宽松的规则可以是完全随意的,并且很容易遵循代码(图像你有一些奇怪的if-else条件和其他人来看在这个代码和奇迹,发生了什么以及为什么)。

You're repeating the same thing too many times, there are two extend calls that are repeated 3 times each. 你重复同样的事情太多次,有两个extend调用,每个重复3次。

You could greatly simplify your code by using the or keyword: 您可以使用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])

Good luck. 祝好运。

Just another way to compare, maybe it can be used in for loop or dict map to help refactor your code, if necessary and performance not important. 只是另一种比较方式,也许它可以用于for循环或dict映射,以帮助重构代码,如果有必要,性能并不重要。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM