【问题标题】:Functional implementation of recursive merge sort?递归合并排序的功能实现?
【发布时间】:2021-04-25 19:03:10
【问题描述】:

尝试在 Python 中实现功能递归合并排序已经有好几天了。除此之外,我希望能够打印出排序算法的每一步。有没有办法让这个 Python 代码以功能范式的方式运行?这就是我目前所拥有的......

def merge_sort(arr, low, high):

    #  If low is less than high, proceed. Else, array is sorted
    if low < high:

        mid = (low + high) // 2             # Get the midpoint

        merge_sort (arr, low, mid)          # Recursively split the left half
        merge_sort (arr, mid+1, high)       # Recursively split the right half
        
        return merge(arr, low, mid, high)   # merge halves together

def merge (arr, low, mid, high):
    temp = []

    # Copy all the values into a temporary array for displaying
    for index, elem in enumerate(arr): 
        temp.append(elem)

    left_p, right_p, i = low, mid+1, low

    # While left and right pointer still have elements to read
    while left_p <= mid and right_p <= high:
        if temp[left_p] <= temp[right_p]:   # If the left value is less than the right value. Shift left pointer by 1 unit to the right
            arr[i] = temp[left_p]
            left_p += 1
        else:                               # Else, place the right value into target array. Shift right pointer by 1 unit to the right
            arr[i] = temp[right_p]
            right_p += 1

        i += 1                              # Increment target array pointer

    # Copy the rest of the left side of the array into the target array
    while left_p <= mid:
        arr[i] = temp[left_p]
        i += 1
        left_p += 1

    print(*arr) # Display the current form of the array

    return arr

def main():
    # Get input from user
    arr = [int(input()) for x in range(int(input("Input the number of elements: ")))]

    print("Sorting...")
    sorted_arr = merge_sort(arr.copy(), 0, len(arr)-1)      
    print("\nSorted Array")
    print(*sorted_arr)

if __name__ == "__main__":
    main()

任何帮助将不胜感激!谢谢。

【问题讨论】:

    标签: python functional-programming mergesort purely-functional


    【解决方案1】:

    在纯函数式归并排序中,我们不想改变任何值。

    我们可以像这样定义一个零变异的递归版本:

    def merge(a1, a2):
      if len(a1) == 0:
        return a2
      if len(a2) == 0:
        return a1
      if a1[0] <= a2[0]:
        rec = merge(a1[1:], a2)
        return [a1[0]] + rec
      rec = merge(a1, a2[1:])
      return [a2[0]] + rec
    
    def merge_sort(arr):
      if len(arr) <= 1:
        return arr
      halfway = len(arr) // 2
      left = merge_sort(arr[:halfway])
      right = merge_sort(arr[halfway:])
      return merge(left, right)
    

    您可以将print(arr) 添加到merge_sort 的顶部以逐步打印,但技术上的副作用会使其不纯(尽管在这种情况下仍然是引用透明的)。但是,在 python 中,您无法使用 monad 将副作用与纯计算分开,因此如果您想真正避免这种打印,则必须返回层,并在最后打印它们 :)

    另外,这个版本在技术上做了很多列表的副本,所以相对来说比较慢。这可以通过使用链表和 consing / unconsing 来解决。但是,这超出了范围。

    【讨论】:

    • @rcgldr 修改链表中的链接是突变,修改节点中的值也是如此。复制列表并改变副本可以避免将参数改变为merge_sort,但这仍然本质上是在编写一个命令式程序,它会改变状态,即使您的状态在您的函数内部也是如此。
    • 创建数组的副本并对其进行变异不是一回事。在我的程序中,我从来没有改变任何东西。这是一种不同的算法和不同的范式。不过,我可以看到您来自哪里,如果我们对两个版本进行黑盒处理,我们会得到相同的结果:) 堆栈是解释器如何计算函数结果的实现细节。如果我们的代码是纯粹的,我们应该能够将其直接转化为大小写匹配和 beta 缩减。有了这段代码,我们就可以了。作为一个haskeller,我会这样写。如果你想要不那么纯粹的东西,那也没关系。
    • 程序计数器的位置也是解释器 /cpu 如何评估您的函数的实现细节。在纯语言中,运行时应该能够以它希望的任何顺序执行没有数据依赖关系的操作。数据依赖是 haskell 提供的唯一保证的求值顺序。
    • “数组或列表的每个值通常通过有序的操作序列转换状态”,你必须在这里更具体,我真的不确定你的意思。如果您需要每一盎司的性能,您也可以选择在 Haskell 中使用复制然后变异排序:hackage.haskell.org/package/vector-algorithms-0.8.0.4/docs/…,实现根本不起作用,但是变异只能发生在 ST monad 和 ST monad 不允许其他副作用,因此仍然有相当强的保证。
    • APL 中,具有a[] 中的值的wiki 示例可以使用((~2|a)/a) +.× 10 完成,其中~ 不是,|是模数,/ 是归约(删除与左侧零相对应的右侧元素),+.× 是“内积”,它将 a[] 的元素乘以 10 并将它们相加,而不指定任何操作顺序(并行化可用于)。 a[]没有被修改,但是过程中修改了内部局部变量。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-18
    • 2016-04-30
    • 2021-11-06
    • 2019-01-22
    相关资源
    最近更新 更多