海象运算符不分配变量?
Walrus Operator Doesn't Assign Variable?
玩海象算子,我实现了归并排序:
def mergesort(array):
if len(array) == 1:
output = array
else:
pivot = len(array) // 2
left = mergesort(array[pivot:])
right = mergesort(array[:pivot])
output = []
while (l := len(left)) or (r := len(right)):
if l and r and left[0] < right[0]:
output.append(left.pop(0))
elif r:
output.append(right.pop(0))
else:
output.append(left.pop(0))
return output
mergesort([66, 93, 85, 46, 56, 88, 56, 75, 55, 99, 87])
但是这个returns错误UnboundLocalError: local variable 'r' referenced before assignment
:
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
/tmp/ipykernel_134/1678992391.py in <module>
----> 1 mergesort(array)
/tmp/ipykernel_134/4030760045.py in mergesort(array)
5 else:
6 pivot = len(array) // 2
----> 7 left = mergesort(array[pivot:])
8 right = mergesort(array[:pivot])
9
...
/tmp/ipykernel_134/4030760045.py in mergesort(array)
10 output = []
11 while (l := len(left)) or (r := len(right)):
---> 12 if l and r and left[0] < right[0]:
13 output.append(left.pop(0))
14 elif r:
UnboundLocalError: local variable 'r' referenced before assignment
为什么 r
不包含在我的 for 循环中,而 l
包含?
- Python 获取
len(left)
表达式并将其计算为 True
- 这意味着它可以立即进入 while 循环而无需检查第二条语句
len(right)
,因为 Python 是 lazy
- 但是,这意味着
r
没有实例化,因为它没有被计算
不幸的是,在表达式两边加上额外的括号没有任何作用。
例如while ( (l:=len(left)) or (r:=len(right)) ):
无效。
解决此问题的一个选项是使用 +
而不是 or
,因为它们具有相同的逻辑含义,但 +
需要计算表达式的右侧.
例如
def mergesort(array):
if len(array) == 1:
output = array
else:
pivot = len(array) // 2
left = mergesort(array[pivot:])
right = mergesort(array[:pivot])
output = []
while (l:=len(left)) + (r:=len(right)):
if l and r and left[0] < right[0]:
output.append(left.pop(0))
elif r:
output.append(right.pop(0))
else:
output.append(left.pop(0))
return output
顺便说一句,如果您在 while X and Y
中遇到同样的问题,其中 X=0
,您可以使用与上述类似的技巧,但不要将 or
替换为 +
你用 *
替换了 and
因为 0 * n == 0
。 IE。 while X * Y
Boolean OR or
是惰性的,所以当 l
结果为真时, (r := len(right))
甚至不会被执行。
在这种情况下,您可以使用非惰性 bitwise OR |
,尽管这有点滥用。
或者只使用列表的 truth value 而不是它们的长度:
while left or right:
if left and right and left[0] < right[0]:
output.append(left.pop(0))
elif right:
output.append(right.pop(0))
else:
output.append(left.pop(0))
顺便说一句,最好使用 <=
而不是 <
,这样它是一个稳定的排序,因为合并排序应该是。
附录:享受懒惰的乐趣:
while left or right:
which = (left or right)[0] <= (right or left)[0] and left or right
output.append(which.pop(0))
另一个,注意我切换到while ... and ...
并在循环后追加剩余的非空:
while left and right:
which = left if left[0] <= right[0] else right
output.append(which.pop(0))
output += left or right
或者回到你的风格:
while left and right:
if left[0] <= right[0]:
output.append(left.pop(0))
else:
output.append(right.pop(0))
output.extend(left or right)
玩海象算子,我实现了归并排序:
def mergesort(array):
if len(array) == 1:
output = array
else:
pivot = len(array) // 2
left = mergesort(array[pivot:])
right = mergesort(array[:pivot])
output = []
while (l := len(left)) or (r := len(right)):
if l and r and left[0] < right[0]:
output.append(left.pop(0))
elif r:
output.append(right.pop(0))
else:
output.append(left.pop(0))
return output
mergesort([66, 93, 85, 46, 56, 88, 56, 75, 55, 99, 87])
但是这个returns错误UnboundLocalError: local variable 'r' referenced before assignment
:
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
/tmp/ipykernel_134/1678992391.py in <module>
----> 1 mergesort(array)
/tmp/ipykernel_134/4030760045.py in mergesort(array)
5 else:
6 pivot = len(array) // 2
----> 7 left = mergesort(array[pivot:])
8 right = mergesort(array[:pivot])
9
...
/tmp/ipykernel_134/4030760045.py in mergesort(array)
10 output = []
11 while (l := len(left)) or (r := len(right)):
---> 12 if l and r and left[0] < right[0]:
13 output.append(left.pop(0))
14 elif r:
UnboundLocalError: local variable 'r' referenced before assignment
为什么 r
不包含在我的 for 循环中,而 l
包含?
- Python 获取
len(left)
表达式并将其计算为True
- 这意味着它可以立即进入 while 循环而无需检查第二条语句
len(right)
,因为 Python 是 lazy - 但是,这意味着
r
没有实例化,因为它没有被计算
不幸的是,在表达式两边加上额外的括号没有任何作用。
例如while ( (l:=len(left)) or (r:=len(right)) ):
无效。
解决此问题的一个选项是使用 +
而不是 or
,因为它们具有相同的逻辑含义,但 +
需要计算表达式的右侧.
例如
def mergesort(array):
if len(array) == 1:
output = array
else:
pivot = len(array) // 2
left = mergesort(array[pivot:])
right = mergesort(array[:pivot])
output = []
while (l:=len(left)) + (r:=len(right)):
if l and r and left[0] < right[0]:
output.append(left.pop(0))
elif r:
output.append(right.pop(0))
else:
output.append(left.pop(0))
return output
顺便说一句,如果您在 while X and Y
中遇到同样的问题,其中 X=0
,您可以使用与上述类似的技巧,但不要将 or
替换为 +
你用 *
替换了 and
因为 0 * n == 0
。 IE。 while X * Y
Boolean OR or
是惰性的,所以当 l
结果为真时, (r := len(right))
甚至不会被执行。
在这种情况下,您可以使用非惰性 bitwise OR |
,尽管这有点滥用。
或者只使用列表的 truth value 而不是它们的长度:
while left or right:
if left and right and left[0] < right[0]:
output.append(left.pop(0))
elif right:
output.append(right.pop(0))
else:
output.append(left.pop(0))
顺便说一句,最好使用 <=
而不是 <
,这样它是一个稳定的排序,因为合并排序应该是。
附录:享受懒惰的乐趣:
while left or right:
which = (left or right)[0] <= (right or left)[0] and left or right
output.append(which.pop(0))
另一个,注意我切换到while ... and ...
并在循环后追加剩余的非空:
while left and right:
which = left if left[0] <= right[0] else right
output.append(which.pop(0))
output += left or right
或者回到你的风格:
while left and right:
if left[0] <= right[0]:
output.append(left.pop(0))
else:
output.append(right.pop(0))
output.extend(left or right)