有没有什么办法可以让这么长的if语句变得更简洁呢?

Is there any way to make this long if statement more concise?

这是我的长 if 语句:

   if i % 2 == 1 or i == 0:
       if x1 > x:
           ny = y + ceil(shift * (i / 2))
           ny1 = y1 + ceil(shift * (i / 2))
       elif x1 == x:
           ny = y
           ny1 = y1
       elif x1 < x:
           ny = y - ceil(shift * (i / 2))
           ny1 = y1 - ceil(shift * (i / 2))
       if y1 > y:
           nx = x - ceil(shift * (i / 2))
           nx1 = x1 - ceil(shift * (i / 2))
       elif y1 == y:
           nx = x
           nx1 = x1
       elif y1 < y:
           nx = x + ceil(shift * (i / 2))
           nx1 = x1 + ceil(shift * (i / 2))
   else:
       if x1 > x:
           ny = y - ceil(shift * (i / 2))
           ny1 = y1 - ceil(shift * (i / 2))
       elif x1 == x:
           ny = y
           ny1 = y1
       elif x1 < x:
           ny = y + ceil(shift * (i / 2))
           ny1 = y1 + ceil(shift * (i / 2))
       if y1 > y:
           nx = x + ceil(shift * (i / 2))
           nx1 = x1 + ceil(shift * (i / 2))
       elif y1 == y:
           nx = x
           nx1 = x1
       elif y1 < y:
           nx = x - ceil(shift * (i / 2))
           nx1 = x1 - ceil(shift * (i / 2))
   return nx, ny, nx1, ny1

我不确定是否可以将整个事情转换成一个简单的方程式。但是,如果没有,是否有任何可能的方法只使用第一个大 if 语句并使 ny = y + ceil(shift * (i / 2)) 中的符号为正 if i % 2 == 1 or i == 0 和负 else?

This guy 是你的朋友。比较主要的 if 和它的 else,我们会看到:

这意味着只有天花板标志在分支之间发生变化。我们可以通过添加一个新变量来解决这个问题:

ceil_sign = 1 if i % 2 == 1 or i == 0 else -1

if x1 > x:
    ny = y + ceil_sign * ceil(shift * (i / 2))
    ny1 = y1 + ceil_sign * ceil(shift * (i / 2))

elif x1 == x:
    ny = y
    ny1 = y1

elif x1 < x:
    ny = y - ceil_sign * ceil(shift * (i / 2))
    ny1 = y1 - ceil_sign * ceil(shift * (i / 2))

if y1 > y:
    nx = x - ceil_sign * ceil(shift * (i / 2))
    nx1 = x1 - ceil_sign * ceil(shift * (i / 2))

elif y1 == y:
    nx = x
    nx1 = x1

elif y1 < y:
    nx = x + ceil_sign * ceil(shift * (i / 2))
    nx1 = x1 + ceil_sign * ceil(shift * (i / 2))

您还可以移动分支:

ceil_sign = 1 if i % 2 == 1 or i == 0 else -1

if x1 > x:
    ny = y + ceil_sign * ceil(shift * (i / 2))
    ny1 = y1 + ceil_sign * ceil(shift * (i / 2))

elif x1 < x:
    ny = y - ceil_sign * ceil(shift * (i / 2))
    ny1 = y1 - ceil_sign * ceil(shift * (i / 2))

else:
    ny = y
    ny1 = y1

if y1 > y:
    nx = x - ceil_sign * ceil(shift * (i / 2))
    nx1 = x1 - ceil_sign * ceil(shift * (i / 2))

elif y1 < y:
    nx = x + ceil_sign * ceil(shift * (i / 2))
    nx1 = x1 + ceil_sign * ceil(shift * (i / 2))

else:
    nx = x
    nx1 = x1

这次重构让我们看到了更多的差异:第一个 if 和它的 elif 也只改变了天花板标志。让我们添加另一个变量:

ceil_sign = 1 if i % 2 == 1 or i == 0 else -1

if x1 == x:
    ny = y
    ny1 = y1

else:
    x_ceil_sign = 1 if x1 > x else -1
    ny = y + x_ceil_sign * ceil_sign * ceil(shift * (i / 2))
    ny1 = y1 + x_ceil_sign * ceil_sign * ceil(shift * (i / 2))

if y1 == y:
    nx = x
    nx1 = x1

else:
    y_ceil_sign = 1 if y1 < y else -1
    nx = x + y_ceil_sign * ceil_sign * ceil(shift * (i / 2))
    nx1 = x1 + y_ceil_sign * ceil_sign * ceil(shift * (i / 2))

正如@Barmar 所建议的,请注意 ceil_sign 总是与 ceil 调用一起出现。您可以将它们组合一次:

ceil_sign = 1 if i % 2 == 1 or i == 0 else -1
ceil_value = ceil_sign * ceil(shift * (i / 2))

if x1 == x:
    ny = y
    ny1 = y1

else:
    x_ceil_sign = 1 if x1 > x else -1
    ny = y + x_ceil_sign * ceil_value
    ny1 = y1 + x_ceil_sign * ceil_value

if y1 == y:
    nx = x
    nx1 = x1

else:
    y_ceil_sign = 1 if y1 < y else -1
    nx = x + y_ceil_sign * ceil_value
    nx1 = x1 + y_ceil_sign * ceil_value

您可以对内部 ceil_signs 执行相同的移动:

ceil_sign = 1 if i % 2 == 1 or i == 0 else -1
ceil_value = ceil_sign * ceil(shift * (i / 2))

if x1 == x:
    ny = y
    ny1 = y1

else:
    x_ceil_sign = (1 if x1 > x else -1) * ceil_value
    ny = y + x_ceil_sign
    ny1 = y1 + x_ceil_sign

if y1 == y:
    nx = x
    nx1 = x1

else:
    y_ceil_sign = (1 if y1 < y else -1) * ceil_value
    nx = x + y_ceil_sign
    nx1 = x1 + y_ceil_sign

请注意,在这两个子句中,您没有在 if 分支中添加任何内容,而在 else 分支中添加 *_ceil_sign。创建另一个变量:

ceil_sign = 1 if i % 2 == 1 or i == 0 else -1
ceil_value = ceil_sign * ceil(shift * (i / 2))

x_factor = 0 if x1 == x else ((1 if x1 > x else -1) * ceil_value)
ny = y + x_factor
ny1 = y1 + x_factor

y_factor = 0 if y1 == y else ((1 if y1 < y else -1) * ceil_value)
nx = x + y_factor
nx1 = x1 + y_factor

作为额外的重构,请注意如果 a > b,sign(a - b) 等于 1。您可以使用它来重构 *_factor 变量:

def sign(x):
    return 0 if x == 0 else 1 if x > 0 else -1

ceil_sign = 1 if i % 2 == 1 or i == 0 else -1
ceil_value = ceil_sign * ceil(shift * (i / 2))

x_factor = 0 if x1 == x else sign(x1 - x) * ceil_value
ny = y + x_factor
ny1 = y1 + x_factor

y_factor = 0 if y1 == y else -sign(y1 - y) * ceil_value
nx = x + y_factor
nx1 = x1 + y_factor

另请注意,如果 a == b,则 sign(a - b) 等于 0,这同时定义了 *_factors:

def sign(x):
    return 0 if x == 0 else 1 if x > 0 else -1

ceil_sign = 1 if i % 2 == 1 or i == 0 else -1
ceil_value = ceil_sign * ceil(shift * (i / 2))

x_factor = sign(x1 - x) * ceil_value
ny = y + x_factor
ny1 = y1 + x_factor

y_factor = -sign(y1 - y) * ceil_value
nx = x + y_factor
nx1 = x1 + y_factor

这里有一些关于如何解决一般问题的提示:

  1. 查找经常重复的表达式,并命名它们。

在这里,ceil(shift * (i / 2))经常发生。让我们命名为:

offset = ceil(shift * (i / 2))
if i % 2 == 1 or i == 0:
    if x1 > x:
        ny = y + offset
        ny1 = y1 + offset
    elif x1 == x:
        ny = y
        ny1 = y1
    elif x1 < x:
        ny = y - offset
        ny1 = y1 - offset
    if y1 > y:
        nx = x - offset
        nx1 = x1 - offset
    elif y1 == y:
        nx = x
        nx1 = x1
    elif y1 < y:
        nx = x + offset
        nx1 = x1 + offset
else:
    if x1 > x:
        ny = y - offset
        ny1 = y1 - offset
    elif x1 == x:
        ny = y
        ny1 = y1
    elif x1 < x:
        ny = y + offset
        ny1 = y1 + offset
    if y1 > y:
        nx = x + offset
        nx1 = x1 + offset
    elif y1 == y:
        nx = x
        nx1 = x1
    elif y1 < y:
        nx = x - offset
        nx1 = x1 - offset
return nx, ny, nx1, ny1
  1. 使用 Python 的 simultaneous assignment 对相关作业进行分组。

这很简单:

offset = ceil(shift * (i / 2))
if i % 2 == 1 or i == 0:
    if x1 > x:
        ny, ny1 = y + offset, y1 + offset
    elif x1 == x:
        ny, ny1 = y, y1
    elif x1 < x:
        ny, ny1 = y - offset, y1 - offset
    if y1 > y:
        nx, nx1 = x - offset, x1 - offset
    elif y1 == y:
        nx, nx1 = x, x1
    elif y1 < y:
        nx, nx1 = x + offset, x1 + offset
else:
    if x1 > x:
        ny, ny1 = y - offset, y1 - offset
    elif x1 == x:
        ny, ny1 = y, y1
    elif x1 < x:
        ny, ny1 = y + offset, y1 + offset
    if y1 > y:
        nx, nx1 = x + offset, x1 + offset
    elif y1 == y:
        nx, nx1 = x, x1
    elif y1 < y:
        nx, nx1 = x - offset, x1 - offset
return nx, ny, nx1, ny1
  1. 利用对称性和相似性。 (我对同步赋值 first 等简单语法进行调整的原因是,通过减少行数,我们可以使对称的事物在视觉上更接近,从而更容易注意到对称性。)

我所说的“对称”或“平行”的意思是我们用不同的变量做基本相同的事情(或者相关的事情有一些小的、有规律的改变)。这里有两个:首先,代码依赖 i % 2 == 1 or i == 0 的方式。我们可以很快看到这个效果 - 通过所有 if/elif 逻辑传播 - 是当条件为假时,offset 被减去,否则它会被添加,并且反之亦然。我们可以使用初等代数来简化它,只需在不满足条件时将 offset 替换为 -offset 即可。因此:

offset = ceil(shift * (i / 2))
if not(i % 2 == 1 or i == 0):
    offset = -offset
if x1 > x:
    ny, ny1 = y + offset, y1 + offset
elif x1 == x:
    ny, ny1 = y, y1
elif x1 < x:
    ny, ny1 = y - offset, y1 - offset
if y1 > y:
    nx, nx1 = x - offset, x1 - offset
elif y1 == y:
    nx, nx1 = x, x1
elif y1 < y:
    nx, nx1 = x + offset, x1 + offset
return nx, ny, nx1, ny1
  1. 进行代数化简。

模式识别在这里很有用。我们正在进行三向比较,并根据该比较添加、减去或不使用给定值。我们可以通过将 offset 分别乘以 1、-1 或 0 来表示“加、减或不使用”。如果我们提取那个想法:

def threeway_compare(a1, a):
    if a1 > a:
        return 1
    elif a1 < a:
        return -1
    return 0 # a1 == a

然后我们找到隐藏的对称性(即我们做了两次那种三向比较):

offset = ceil(shift * (i / 2))
if not(i % 2 == 1 or i == 0):
    offset = -offset
y_offset = threeway_compare(x1, x)
ny, ny1 = y + y_offset, y1 + y_offset
# Since the changes made to the nx/nx1 values were reversed,
# we negate the comparison result before using it this way.
x_offset = -threeway_compare(y1, y)
nx, nx1 = x + x_offset, x1 + x_offset
return nx, ny, nx1, ny1
  1. 舍弃一切,尝试不同的方法。

有时需要这样做。在这种特殊情况下,使用 ceil 来计算“调整”是出现问题的警告信号。直接计算 nx 等值可能更好,而不是计算 x 等然后必须以这种方式调整它们。也许这表明原始问题有问题。