【问题标题】:Avoiding stack overflow with functional programming使用函数式编程避免堆栈溢出
【发布时间】:2022-07-17 20:59:00
【问题描述】:

我正在尝试使用函数式编程解决黑客等级的“网格搜索”问题。请查看hackerrank上的问题描述。 https://www.hackerrank.com/challenges/the-grid-search/problem

我只想使用递归和函数式编程原语,例如 map、filter 等。我当前的解决方案检查它是否可以在数组的开头找到模式,如果不能,那么我递归调用在数组的尾部。我想出了以下几点:

def checkLine(str1, str2):
    return str1 in str2

def checkGroup(group1, group2):
    return False not in list(map(lambda x: checkLine(x[0], x[1]), zip(group1, group2)))

def gridSearch(G, P):
    # Write your code here
    if len(G) < len(P):
        return "NO"
    if checkGroup(P, G):
        return "YES"
    else:
        return gridSearch(G[1:], P)

问题是当两个数组都非常大时,我遇到了堆栈溢出。我知道您可以通过使用尾递归来避免堆栈溢出,但我不太确定如何在这里实现。谁能举例说明如何在功能上解决这个问题,同时避免堆栈溢出?

【问题讨论】:

  • 您可以增加递归限制,但这样做会导致 HackerRank 返回错误答案。你确定你的代码是正确的吗?
  • " 我知道你可以通过使用尾递归来避免堆栈溢出" 你不能。 CPython 不进行尾调用优化。实际上,您的网格搜索功能已经是尾递归的。从根本上说,当你想使用深度递归时,你应该只在 Python 中使用迭代。 Python 不是函数式编程语言(尽管它借鉴了范式,例如列表推导式)。
  • @BrokenBenchmark 它通过了大约 10 个测试用例,但其他输入非常大的测试用例却失败了。感谢您的回复。我现在看到我写的函数已经是尾递归了。

标签: python recursion functional-programming


【解决方案1】:

我并不是真正的 Python 程序员,所以可能有更惯用的方法来做这件事,但在 JavaScript 中我会使用一种称为 trampolining 的技术:

def trampoline(original_function):
    def wrapped_function(*args):
        # Call the original function
        result = original_function(*args)
        # If a function is returned, call the function until
        # a non-function is returned, which is the final result
        while callable(result):
            result = result()
        return result
    return wrapped_function

# Factorial function example
def _fact(n, acc=1):
    if n == 0: return acc
    return lambda: _fact(n - 1, n * acc)
fact = trampoline(_fact)

def _gridSearch(G, P):
    # Write your code here
    if len(G) < len(P):
        return "NO"
    if checkGroup(P, G):
        return "YES"
    else:
        # note the lambda: here
        return lambda: _gridSearch(G[1:], P)
gridSearch = trampoline(_gridSearch)

我个人不喜欢用if callable(result)来检查递归函数是否输出了最终值,所以另一种方法可以是这样:

from dataclasses import dataclass

@dataclass
class Recurse:
    value: ...

@dataclass
class Return:
    value: ...

def trampoline(fn):
    def f(*args):
        x = fn(*args)
        while isinstance(x, Recurse):
            x = fn(*x.value)
        return x.value
    return f

# Factorial function example
def _fact(n, acc=1):
    if n == 0: return Return(acc)
    return Recurse((n - 1, n * acc))
fact = trampoline(_fact)

def _gridSearch(G, P):
    # Write your code here
    if len(G) < len(P):
        return Return("NO")
    if checkGroup(P, G):
        return Return("YES")
    else:
        return Recurse((G[1:], P))
gridSearch = trampoline(_gridSearch)

这种方式比另一种更通用,因为它适用于返回另一个函数的递归函数(但我想不出你为什么需要这样做)。

【讨论】:

    猜你喜欢
    • 2016-07-12
    • 2020-03-08
    • 1970-01-01
    • 1970-01-01
    • 2010-11-30
    • 1970-01-01
    • 2013-06-28
    • 2011-09-21
    • 2015-11-14
    相关资源
    最近更新 更多