Python: 写一段相当复杂的代码作为列表理解
Python: Writing quite complicated piece of code as a List Comprehension
我已经用下面的代码解决了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
而我无法通过列表理解来做到这一点。
我知道写一大段代码作为列表理解不是一个好主意(我已经很困惑了)但这只是为了教育目的。
非常感谢任何有关正确编写此列表理解的帮助。
确实,因为 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 函数解决此问题,该函数既改变又 return 某些东西:
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
), 或 return 一个值,但不是两者。此规则只有少数例外(例如 .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]
我已经用下面的代码解决了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
而我无法通过列表理解来做到这一点。
我知道写一大段代码作为列表理解不是一个好主意(我已经很困惑了)但这只是为了教育目的。
非常感谢任何有关正确编写此列表理解的帮助。
确实,因为 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 函数解决此问题,该函数既改变又 return 某些东西:
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
), 或 return 一个值,但不是两者。此规则只有少数例外(例如 .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]