【问题标题】:Python: Writing quite complicated piece of code as a List ComprehensionPython:编写相当复杂的代码作为列表理解
【发布时间】:2020-09-29 10:18:39
【问题描述】:

我已经用下面的代码解决了Word Break的问题:

def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        n = len(s)
        dp = [False] * n
        for i in range(n):
            for j in range(i + 1, n + 1):
                if (s[i : j] in wordDict) and (i == 0 or dp[i - 1]):
                    dp[j - 1] = True
        return dp[n - 1]

我正在尝试将其编写为列表理解,以使其“或多或少一行代码”。

我一直在尝试将dp表达如下:

dp = [True if (s[i:j] in wordDict) and (i==0 or dp[i-1]) for j in range(i+1, n+1) for i in range(n) else False]

但是,我有点卡住了,因为在原始代码中它设置为dp[j - 1] = True,而我无法通过列表理解来实现。

我知道编写一大段代码作为列表理解不是一个好主意(我也很困惑),但这只是为了教育目的

非常感谢任何有关正确编写此列表理解的帮助。

【问题讨论】:

  • 您能否提供您提供给代码的示例输入?目前还不清楚您的代码是做什么的。
  • @AmitSingh 添加了问题的链接。
  • 列表理解的主要要求是可以访问一个可迭代对象,因此除非您以某种方式将其转换为可迭代对象,否则很难用正确的方法实现您想要做的事情。因此,我们将不得不重写您可能会多次更改 d[j-1] 值的部分。
  • 按原样,这不能重写为列表推导,因为您在循环中引用了dp[i - 1]。作为一个列表推导,变量dp 甚至不会在那个时候被声明。

标签: python for-loop list-comprehension dynamic-programming


【解决方案1】:

事实上,dp 被写入,不仅如此,而且那些新写入的值在下一次迭代中再次读取,这将不是列表理解的候选者。 但是,如果您愿意牺牲一些 Python 的最佳实践,那么您可以接近。

首先,您可以将内部循环转换为列表推导:

def wordBreak(self, s: str, wordDict: List[str]) -> bool:
    n = len(s)
    dp = [False] * n
    for i in range(n):
        dp[i:] = [dp[j] or
                   (s[i:j+1] in wordDict) and (i == 0 or dp[i-1])
                   for j in range(i, n)]
    return dp[-1]

让我们稍微改变一下算法,所以列表覆盖从索引 0 开始,而不是从索引 i 开始。相反,我们逐渐缩短列表。同时我们将dp[0]指定为从上一次迭代中读取的值,因此我们也将其添加到初始列表的前缀:

def wordBreak(self, s: str, wordDict: List[str]) -> bool:
    n = len(s)
    dp = [True] + [False] * n
    for i in range(n):
        dp = [dp[j-i+1] or s[i:j+1] in wordDict and dp[0]
                   for j in range(i, n)]
    return dp[0]

但是dp = 是一个原则上阻止更广泛使用列表理解的赋值。 您可以使用 a-Pythonic 函数解决此问题,该函数既可以更改也可以返回一些内容:

def assign(self, target, source):
    target[:] = source  # mutate the target
    return target[-1]  # for our purposes we only need it to return the last value

现在我们可以将它包含在一个更大的列表理解表达式中:

def wordBreak(self, s: str, wordDict: List[str]) -> bool:
    n = len(s)
    dp = [True] + [False] * n
    return [
        self.assign(dp, [dp[j-i+1] or s[i:j+1] in wordDict and dp[0]
                        for j in range(i, n)])
                        for i in range(n)
    ][-1]

但要重复一遍:这不是pythonic。最佳实践是让函数或者改变一个参数(或self),返回一个值,但不能同时返回。此规则只有少数例外(例如.pop()

如果您对函数表达式感到满意,而不是列表推导式,则可以使用functools.reduce

def wordBreak(self, s: str, wordDict: List[str]) -> bool:
    n = len(s)
    return reduce(lambda dp, i: [dp[j-i+1] or s[i:j+1] in wordDict and dp[0]
                                 for j in range(i, n)], 
                  range(n), 
                  [True] + [False] * n)[0]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-12
    • 2020-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多