很简单,什么是尾部调用优化?更具体地说,任何人都可以显示一些可以应用的小代码片段,而不是在哪里,并解释为什么?
language-agnostic algorithm recursion tail-recursion tail-call-optimization
我有以下代码来执行此操作,但我怎样才能做得更好?现在我认为它比嵌套循环更好,但是当你在列表理解中有一个生成器时,它开始得到Perl-one-liner.
day_count = (end_date - start_date).days + 1
for single_date in [d for d in (start_date + timedelta(n) for n in range(day_count)) if d <= end_date]:
print strftime("%Y-%m-%d", single_date.timetuple())
Run Code Online (Sandbox Code Playgroud)
start_date和end_date变量是datetime.date因为我不需要时间戳对象.(它们将用于生成报告).对于开始日期2009-05-30和结束日期2009-06-09:
2009-05-30
2009-05-31
2009-06-01
2009-06-02
2009-06-03
2009-06-04
2009-06-05
2009-06-06
2009-06-07
2009-06-08
2009-06-09
Run Code Online (Sandbox Code Playgroud) 功能语言导致使用递归来解决许多问题,因此许多函数执行尾调用优化(TCO).TCO导致从另一个函数调用函数(或者本身,在这种情况下,这个特性也称为Tail Recursion Elimination,它是TCO的一个子集),作为该函数的最后一步,不需要新的堆栈帧,这减少了开销和内存使用.
Ruby显然已经从函数式语言(lambdas,map等函数等)中"借用"了许多概念,这让我很好奇:Ruby是否执行尾调用优化?
我有一个尾递归寻路算法,我已经在Javascript中实现,并想知道是否有任何(所有?)浏览器可能会得到堆栈溢出异常.
假设我们要计算一些斐波那契数,以 997 为模。
因为n=500在 C++ 中我们可以运行
#include <iostream>
#include <array>
std::array<int, 2> fib(unsigned n) {
if (!n)
return {1, 1};
auto x = fib(n - 1);
return {(x[0] + x[1]) % 997, (x[0] + 2 * x[1]) % 997};
}
int main() {
std::cout << fib(500)[0];
}
Run Code Online (Sandbox Code Playgroud)
在 Python 中
def fib(n):
if n==1:
return (1, 2)
x=fib(n-1)
return ((x[0]+x[1]) % 997, (x[0]+2*x[1]) % 997)
if __name__=='__main__':
print(fib(500)[0])
Run Code Online (Sandbox Code Playgroud)
两者都可以毫无问题地找到答案 996。我们采用模数来保持合理的输出大小,并使用对来避免指数分支。
对于n=5000,C++ 代码输出 783,但 Python 会抱怨
RecursionError: …Run Code Online (Sandbox Code Playgroud) 我知道Python不是Haskell或Ocaml,但这是在Python(2或3)中定义代数数据类型的最佳方法吗?谢谢!
我试图生成所有可能的方法来交错Python中的任意两个任意字符串.
例如:如果两个字符串是'ab'和'cd',我希望获得的输出是:
['abcd', 'acbd', 'acdb', 'cabd', 'cadb', 'cdab']
Run Code Online (Sandbox Code Playgroud)
a总是在b(c之前d)之前看到.我正在努力寻找解决方案.我尝试过如下所示的itertools:
import itertools
def shuffle(s,t):
string = s+t
for i in itertools.permutations(string):
print(''.join(i))
shuffle('ab','cd')
Run Code Online (Sandbox Code Playgroud)
但正如预期的那样,这将返回所有可能的排列,而忽略了a和b(c和d)的顺序.
Python具有最大递归深度,但没有最大迭代深度.为什么递归受限制?像迭代这样处理递归是不是更自然,而不是限制递归调用的数量?
我只想说这个问题的根源来自于尝试实现一个流(有关流的更多详细信息,请参阅此问题).例如,假设我们要编写一个流来生成自然数:
def stream_accum(s, n): # force the stream to a list of length n
def loop(s, acc):
if len(acc) == n:
return acc
hd, tl = s()
return loop(tl, acc + [hd])
return loop(s, [])
def nats():
def loop(n):
return n, lambda: loop(n+1)
return loop(1)
Run Code Online (Sandbox Code Playgroud)
流的递归定义非常吸引人.但是,我想更好/更pythonic的方法是使用发电机.
我有一些基本上看起来像这样的Python代码:
my_start_list = ...
def process ( my_list ):
#do some stuff
if len(my_list) > 1:
process(my_list)
else:
print(my_list)
return my_list
print(process(my_start_list))
Run Code Online (Sandbox Code Playgroud)
奇怪的是:print(my_list)打印出正确的内容.但是,打印函数返回值的第二个print语句始终打印"None".即使我用return("abc")替换正常的return语句,它仍然是None.
由于变量的内容在return语句之前似乎是正确的一行,我不知道从哪里开始调试.是否有可能导致此问题的常见问题?
我已经在python中编写了一个verilog(逻辑门及其连接描述)模拟器作为实验的一部分.
我遇到了堆栈限制的问题,所以我做了一些阅读,发现Python没有"尾调用优化"功能(即随着递归的进行动态删除堆栈条目)
我在这方面主要有两个问题:
1)如果我提高了堆栈限制,sys.setrecursionlimit(15000)它是否会影响性能(内存 - 我不在乎)?
2)我有什么方法可以绕过这个限制,假设我可以没有堆栈跟踪.
我问这个是因为Verilog主要处理可以使用递归函数以优雅方式实现的状态机.
另外,如果我可以添加,在递归函数调用的情况下,如果有错误,我更依赖于导致此错误的输入而不是堆栈跟踪.
我是Python新手,所以也许专家可能认为Python堆栈跟踪对于调试递归函数调用非常有用......如果是这种情况,我会非常乐意学习如何做到这一点.
最后,建议在Python中编写递归函数还是应该转移到其他语言?
如果有任何解决方法,我可以继续使用python进行递归函数,我想知道是否有任何性能影响(我可以做分析).