if/elsif/else return Ruby 和 Python 之间的行为差异
if/elsif/else return behavior difference between Ruby and Python
我正在尝试将快速选择从 Ruby 转换为 Python。 Ruby代码如下:
class SortableArray
attr_reader :array
def initialize(array)
@array = array
end
def quickselect!(kth_lowest_value, left_index, right_index)
# If we reach a base case - that is, that the subarray has one cell,
# we know we've found the value we're looking for:
if right_index - left_index <= 0
return @array[left_index]
end
# Partition the array and grab the index of the pivot:
pivot_index = partition!(left_index, right_index)
# If what we're looking for is to the left of the pivot:
if kth_lowest_value < pivot_index
# Recursively perform quickselect on the subarray to
# the left of the pivot:
quickselect!(kth_lowest_value, left_index, pivot_index - 1)
# If what we're looking for is to the right of the pivot:
elsif kth_lowest_value > pivot_index
# Recursively perform quickselect on the subarray to
# the right of the pivot:
quickselect!(kth_lowest_value, pivot_index + 1, right_index)
else # if kth_lowest_value == pivot_index
# if after the partition, the pivot position is in the same spot
# as the kth lowest value, we've found the value we're looking for
return @array[pivot_index]
end
end
def partition!(left_pointer, right_pointer)
# We always choose the right-most element as the pivot.
# We keep the index of the pivot for later use:
pivot_index = right_pointer
# We grab the pivot value itself:
pivot = @array[pivot_index]
# We start the right pointer immediately to the left of the pivot
right_pointer -= 1
while true
# Move the left pointer to the right as long as it
# points to value that is less than the pivot:
while @array[left_pointer] < pivot do
left_pointer += 1
end
# Move the right pointer to the left as long as it
# points to a value that is greater than the pivot:
while @array[right_pointer] > pivot do
right_pointer -= 1
end
# We've now reached the point where we've stopped
# moving both the left and right pointers.
# We check whether the left pointer has reached
# or gone beyond the right pointer. If it has,
# we break out of the loop so we can swap the pivot later
# on in our code:
if left_pointer >= right_pointer
break
# If the left pointer is still to the left of the right
# pointer, we swap the values of the left and right pointers:
else
@array[left_pointer], @array[right_pointer] = @array[right_pointer], @array[left_pointer]
# We move the left pointer over to the right, gearing up
# for the next round of left and right pointer movements:
left_pointer += 1
end
end
# As the final step of the partition, we swap the value
# of the left pointer with the pivot:
@array[left_pointer], @array[pivot_index] = @array[pivot_index], @array[left_pointer]
# We return the left_pointer for the sake of the quicksort method
# which will appear later in this chapter:
return left_pointer
end
end
array = [0, 50, 20, 10, 60, 30]
sortable_array = SortableArray.new(array)
p sortable_array.quickselect!(1, 0, array.length - 1)
当我将其转换为 python 时,只有当我在 if 和 elif 条件(第 28 和 30 行)之后放置一个 return
时它才有效,如下所示:
def partition(left_p, right_p, arr=[]):
pivot_index = right_p
pivot = arr[pivot_index]
right_p -= 1
while True:
while arr[left_p] < pivot:
left_p += 1
while arr[right_p] > pivot:
right_p -= 1
if left_p >= right_p:
break
else:
arr[left_p], arr[right_p] = arr[right_p], arr[left_p]
left_p += 1
arr[left_p], arr[pivot_index] = arr[pivot_index], arr[left_p]
return left_p
def quickselect(kth_lowest_value, left_index, right_index, arr=[]):
print(arr)
if right_index - left_index <= 0:
return arr[left_index]
pivot_index = partition(left_index, right_index, arr)
if kth_lowest_value < pivot_index:
return quickselect(kth_lowest_value, left_index, pivot_index-1, arr)
elif kth_lowest_value > pivot_index:
return quickselect(kth_lowest_value, pivot_index+1, right_index, arr)
else:
print(f"item = {arr[pivot_index]}")
return arr[pivot_index]
array = [200, 97, 100, 101, 211, 107, 63, 123, 11, 34]
index = quickselect(6, 0, len(array)-1, array)
print(index)
为什么在没有 return 的 Ruby 中也能正常工作?是因为在class里面吗?
Ruby 代码不是很地道。 if
-elsif
-else
分支中的最后一个 return
是转移注意力,与条件链中的其他分支不一致。在 if
-elsif
-else
链的其他分支上也有一个隐含的 return
。
概括上述思想,在所有Ruby函数和方法中,如果控制到达函数末尾而没有遇到return
,则return值为评估的最后一个表达式。在条件链中,这就是采用的分支。
一个最小的例子是:
def foo(n)
if n == 0
"zero"
elsif n == 1
"one"
else
"something else"
end
end
puts foo(0) # => zero
puts foo(1) # => one
puts foo(2) # => something else
将 return
添加到任何或所有上述分支不会改变行为,通常会被省略。
另一方面,Python 的隐式 return 始终是 None
。返回非 None
值涉及使用显式 return
(我猜是 "explicit is better than implicit")。
def foo(n):
if n == 0:
return "zero"
elif n == 1:
return "one"
else:
return "something else"
print(foo(0)) # => zero
print(foo(1)) # => one
print(foo(2)) # => something else
关于 Ruby 代码的另一个注意事项:通常 !
在 function/method 名称之后用于 in-place 算法,即修改输入的算法,或者否则更危险的非 bang 方法版本。由于 quickselect!
不修改任何 class 状态,所以它有一个爆炸令人困惑。
我正在尝试将快速选择从 Ruby 转换为 Python。 Ruby代码如下:
class SortableArray
attr_reader :array
def initialize(array)
@array = array
end
def quickselect!(kth_lowest_value, left_index, right_index)
# If we reach a base case - that is, that the subarray has one cell,
# we know we've found the value we're looking for:
if right_index - left_index <= 0
return @array[left_index]
end
# Partition the array and grab the index of the pivot:
pivot_index = partition!(left_index, right_index)
# If what we're looking for is to the left of the pivot:
if kth_lowest_value < pivot_index
# Recursively perform quickselect on the subarray to
# the left of the pivot:
quickselect!(kth_lowest_value, left_index, pivot_index - 1)
# If what we're looking for is to the right of the pivot:
elsif kth_lowest_value > pivot_index
# Recursively perform quickselect on the subarray to
# the right of the pivot:
quickselect!(kth_lowest_value, pivot_index + 1, right_index)
else # if kth_lowest_value == pivot_index
# if after the partition, the pivot position is in the same spot
# as the kth lowest value, we've found the value we're looking for
return @array[pivot_index]
end
end
def partition!(left_pointer, right_pointer)
# We always choose the right-most element as the pivot.
# We keep the index of the pivot for later use:
pivot_index = right_pointer
# We grab the pivot value itself:
pivot = @array[pivot_index]
# We start the right pointer immediately to the left of the pivot
right_pointer -= 1
while true
# Move the left pointer to the right as long as it
# points to value that is less than the pivot:
while @array[left_pointer] < pivot do
left_pointer += 1
end
# Move the right pointer to the left as long as it
# points to a value that is greater than the pivot:
while @array[right_pointer] > pivot do
right_pointer -= 1
end
# We've now reached the point where we've stopped
# moving both the left and right pointers.
# We check whether the left pointer has reached
# or gone beyond the right pointer. If it has,
# we break out of the loop so we can swap the pivot later
# on in our code:
if left_pointer >= right_pointer
break
# If the left pointer is still to the left of the right
# pointer, we swap the values of the left and right pointers:
else
@array[left_pointer], @array[right_pointer] = @array[right_pointer], @array[left_pointer]
# We move the left pointer over to the right, gearing up
# for the next round of left and right pointer movements:
left_pointer += 1
end
end
# As the final step of the partition, we swap the value
# of the left pointer with the pivot:
@array[left_pointer], @array[pivot_index] = @array[pivot_index], @array[left_pointer]
# We return the left_pointer for the sake of the quicksort method
# which will appear later in this chapter:
return left_pointer
end
end
array = [0, 50, 20, 10, 60, 30]
sortable_array = SortableArray.new(array)
p sortable_array.quickselect!(1, 0, array.length - 1)
当我将其转换为 python 时,只有当我在 if 和 elif 条件(第 28 和 30 行)之后放置一个 return
时它才有效,如下所示:
def partition(left_p, right_p, arr=[]):
pivot_index = right_p
pivot = arr[pivot_index]
right_p -= 1
while True:
while arr[left_p] < pivot:
left_p += 1
while arr[right_p] > pivot:
right_p -= 1
if left_p >= right_p:
break
else:
arr[left_p], arr[right_p] = arr[right_p], arr[left_p]
left_p += 1
arr[left_p], arr[pivot_index] = arr[pivot_index], arr[left_p]
return left_p
def quickselect(kth_lowest_value, left_index, right_index, arr=[]):
print(arr)
if right_index - left_index <= 0:
return arr[left_index]
pivot_index = partition(left_index, right_index, arr)
if kth_lowest_value < pivot_index:
return quickselect(kth_lowest_value, left_index, pivot_index-1, arr)
elif kth_lowest_value > pivot_index:
return quickselect(kth_lowest_value, pivot_index+1, right_index, arr)
else:
print(f"item = {arr[pivot_index]}")
return arr[pivot_index]
array = [200, 97, 100, 101, 211, 107, 63, 123, 11, 34]
index = quickselect(6, 0, len(array)-1, array)
print(index)
为什么在没有 return 的 Ruby 中也能正常工作?是因为在class里面吗?
Ruby 代码不是很地道。 if
-elsif
-else
分支中的最后一个 return
是转移注意力,与条件链中的其他分支不一致。在 if
-elsif
-else
链的其他分支上也有一个隐含的 return
。
概括上述思想,在所有Ruby函数和方法中,如果控制到达函数末尾而没有遇到return
,则return值为评估的最后一个表达式。在条件链中,这就是采用的分支。
一个最小的例子是:
def foo(n)
if n == 0
"zero"
elsif n == 1
"one"
else
"something else"
end
end
puts foo(0) # => zero
puts foo(1) # => one
puts foo(2) # => something else
将 return
添加到任何或所有上述分支不会改变行为,通常会被省略。
Python 的隐式 return 始终是 None
。返回非 None
值涉及使用显式 return
(我猜是 "explicit is better than implicit")。
def foo(n):
if n == 0:
return "zero"
elif n == 1:
return "one"
else:
return "something else"
print(foo(0)) # => zero
print(foo(1)) # => one
print(foo(2)) # => something else
关于 Ruby 代码的另一个注意事项:通常 !
在 function/method 名称之后用于 in-place 算法,即修改输入的算法,或者否则更危险的非 bang 方法版本。由于 quickselect!
不修改任何 class 状态,所以它有一个爆炸令人困惑。