`
simohayha
  • 浏览: 1395640 次
  • 性别: Icon_minigender_1
  • 来自: 火星
社区版块
存档分类
最新评论

python中是否可以实现'tail-call elimination'的优化

阅读更多
今天看 python in nutshell 时看到了这段

Reader who are familiar Lisp, Scheme, or functional-programming languages must in particular be aware that Python does not implement the optimization of "tail-call elimination," which is so important in these languages. In Python, any call, recursive or not, has the same cost in terms of both time and memory space, dependent only on the number of arguments: the cost does not change, whether the call is a "tail-call" (meaning that the call is the last operation that the caller executes) or any other, nontail call.

说python中不能实现optimization of "tail-call elimination,"可是我google中搜索了一下发现了这个

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/474088

可是由于不太了解functional-programming  所以the optimization of "tail-call elimination" 也不知道确切的意思,所以也不知道下面的代码写的对不对。

下面的代码是上面的链接里面的那个人写的.
#!/usr/bin/env python2.4
# This program shows off a python decorator(
# which implements tail call optimization. It
# does this by throwing an exception if it is 
# it's own grandparent, and catching such 
# exceptions to recall the stack.

import sys

class TailRecurseException:
  def __init__(self, args, kwargs):
    self.args = args
    self.kwargs = kwargs

def tail_call_optimized(g):
  """
  This function decorates a function with tail call
  optimization. It does this by throwing an exception
  if it is it's own grandparent, and catching such
  exceptions to fake the tail call optimization.
  
  This function fails if the decorated
  function recurses in a non-tail context.
  """
  def func(*args, **kwargs):
    f = sys._getframe()
    if f.f_back and f.f_back.f_back \
        and f.f_back.f_back.f_code == f.f_code:
      raise TailRecurseException(args, kwargs)
    else:
      while 1:
        try:
          return g(*args, **kwargs)
        except TailRecurseException, e:
          args = e.args
          kwargs = e.kwargs
  func.__doc__ = g.__doc__
  return func

@tail_call_optimized
def factorial(n, acc=1):
  "calculate a factorial"
  if n == 0:
    return acc
  return factorial(n-1, n*acc)

print factorial(10000)
# prints a big, big number,
# but doesn't hit the recursion limit.

@tail_call_optimized
def fib(i, current = 0, next = 1):
  if i == 0:
    return current
  else:
    return fib(i - 1, next, current + next)

print fib(10000)
# also prints a big number,
# but doesn't hit the recursion limit.






分享到:
评论
17 楼 cookoo 2007-06-12  
gigix 写道
cookoo 写道
simohayha 写道
cookoo 写道
不支持也没什么,Ruby也没有,Java spec也不要求。尾递归就是个loop, 直接写loop好了。FP不鼓励写loop那是另外一回事。


哦,原来这样,为什么不鼓励写loop呢,还有就是python中的异常开销是不是不大?

因为传统上循环总需要个计数器,也就是side effect拉。所以FP鼓励用递归或list comprehension,有点洁癖之嫌吧?

至少有一个最新的理由:
函数式的列表操作(map/reduce/filter)是可以拆分的
比如reduce,你可以把一个大列表分成几个小列表,分别对它们做reduce,得到的结果放进同一个列表再做reduce,效果是一样的
map,更是如此
filter,同样
所以你就可以把“分解成小列表的操作”放在几个核上运行
这样就可以充分利用多核的计算能力

大部分情况都如你所说,不过也有例外,比如前后依赖的lazy list:
fibs = 0 : 1 : [ a + b | (a, b) <- zip fibs (tail fibs)]

另外我觉得这是实现细节,聪明点的编译器也许能辨别能否把loop拆开来并行,比如考虑矩阵相乘操作。
16 楼 gigix 2007-06-11  
cookoo 写道
simohayha 写道
cookoo 写道
不支持也没什么,Ruby也没有,Java spec也不要求。尾递归就是个loop, 直接写loop好了。FP不鼓励写loop那是另外一回事。


哦,原来这样,为什么不鼓励写loop呢,还有就是python中的异常开销是不是不大?

因为传统上循环总需要个计数器,也就是side effect拉。所以FP鼓励用递归或list comprehension,有点洁癖之嫌吧?

至少有一个最新的理由:
函数式的列表操作(map/reduce/filter)是可以拆分的
比如reduce,你可以把一个大列表分成几个小列表,分别对它们做reduce,得到的结果放进同一个列表再做reduce,效果是一样的
map,更是如此
filter,同样
所以你就可以把“分解成小列表的操作”放在几个核上运行
这样就可以充分利用多核的计算能力
15 楼 simohayha 2007-06-11  
cookoo 写道
simohayha 写道
cookoo 写道
不支持也没什么,Ruby也没有,Java spec也不要求。尾递归就是个loop, 直接写loop好了。FP不鼓励写loop那是另外一回事。


哦,原来这样,为什么不鼓励写loop呢,还有就是python中的异常开销是不是不大?

因为传统上循环总需要个计数器,也就是side effect拉。所以FP鼓励用递归或list comprehension,有点洁癖之嫌吧?
恩,map的存在可能也是一个原因.
14 楼 cookoo 2007-06-11  
simohayha 写道
cookoo 写道
不支持也没什么,Ruby也没有,Java spec也不要求。尾递归就是个loop, 直接写loop好了。FP不鼓励写loop那是另外一回事。


哦,原来这样,为什么不鼓励写loop呢,还有就是python中的异常开销是不是不大?

因为传统上循环总需要个计数器,也就是side effect拉。所以FP鼓励用递归或list comprehension,有点洁癖之嫌吧?
13 楼 simohayha 2007-03-02  
Arbow 写道
我说python的异常处理是不是开销不大?Java里面可不敢这样搞


呵呵,python in nutshell 有这么一句

引用

In Python, exceptions are considered appropriate whenever they make a program simpler and more robust, even if that means that exceptions are raised rather frequently
12 楼 simohayha 2007-02-26  
cookoo 写道
不支持也没什么,Ruby也没有,Java spec也不要求。尾递归就是个loop, 直接写loop好了。FP不鼓励写loop那是另外一回事。


哦,原来这样,为什么不鼓励写loop呢,还有就是python中的异常开销是不是不大?
11 楼 cookoo 2007-02-15  
不支持也没什么,Ruby也没有,Java spec也不要求。尾递归就是个loop, 直接写loop好了。FP不鼓励写loop那是另外一回事。
10 楼 simohayha 2007-02-14  
这个我就不懂了。应该是开销不大吧?
9 楼 Arbow 2007-02-14  
我说python的异常处理是不是开销不大?Java里面可不敢这样搞
8 楼 simohayha 2007-02-14  

if f.f_back and f.f_back.f_back and f.f_back.f_back.f_code == f.f_code


nnd总算我也懂了,原来把and的计算规则忘了,嘿嘿。

x and y ,if x is false 结果就是x 否则结果是 y.

7 楼 Arbow 2007-02-14  
sys._getframe:
引用
_getframe( [depth])

Return a frame object from the call stack. If optional integer depth is given, return the frame object that many calls below the top of the stack. If that is deeper than the call stack, ValueError is raised. The default for depth is zero, returning the frame at the top of the call stack.
This function should be used for internal and specialized purposes only.


Frame Object:
引用
Frame objects
Frame objects represent execution frames. They may occur in traceback objects (see below).
Special read-only attributes: f_back is to the previous stack frame (towards the caller), or None if this is the bottom stack frame; f_code is the code object being executed in this frame; f_locals is the dictionary used to look up local variables; f_globals is used for global variables; f_builtins is used for built-in (intrinsic) names; f_restricted is a flag indicating whether the function is executing in restricted execution mode; f_lasti gives the precise instruction (this is an index into the bytecode string of the code object).


总算看懂了,
if f.f_back and f.f_back.f_back and f.f_back.f_back.f_code == f.f_code
当属于自身的递归调用(fib()调用fib())时,上面语句返回true,抛出个 TailRecurseException,然后下面的代码截取异常后直接执行函数。这样就不需要用到堆栈了,也不会溢出。用异常来做控制流啊 多少次递归操作就会抛出多少次异常
6 楼 simohayha 2007-02-13  
呵呵,他这边注释说 This function fails if the decorated 
  function recurses in a non-tail context 。

if f.f_back and f.f_back.f_back \      
    and f.f_back.f_back.f_code == f.f_code:      
  raise TailRecurseException(args, kwargs)


这段代码我也不懂什么意思
注释也看不太懂

It does this by throwing an exception 
  if it is it's own grandparent

nnd,好郁闷运行了下10000,显卡驱动直接挂掉,无语了.

5 楼 Arbow 2007-02-13  
你给的那段代码:
@tail_call_optimized
标记了这个的function可以被tail_call_optimized这个方法proxy,查看tail_call_optimized(g),它应该是先判断此方法是否为尾递归类型?我也不太清楚
    if f.f_back and f.f_back.f_back \   
        and f.f_back.f_back.f_code == f.f_code:   
      raise TailRecurseException(args, kwargs)  

如果是,则执行这个函数,当抛出TailRecurseException时就截获它,然后再次继续执行下去,避免堆栈不够的问题。
    else:   
      while 1:   
        try:   
          return g(*args, **kwargs)   
        except TailRecurseException, e:   
          args = e.args   
          kwargs = e.kwargs   

尾递归方式的函数,执行起来还是跟迭代差不多快的,主要是python不执行这种优化,有stack容量限制,这个proxy就解决它了,比较精妙,收藏一下
4 楼 simohayha 2007-02-13  
python 中的递归是有 限制的,默认情况下over a depth of 1,000就会抛出异常,可是你还能通过sys模块的setrecursionlimit来设置递归的限制,不过这个设置也是有限制的,它依赖于你的操作系统和c的库,文档里面好像说 大概不会超过 几千层.

呵呵那段代码现在基本能看懂了.

ps:不知道为什么python不把 尾递归优化 加进去,嘿嘿,希望下个版本能加进去.
3 楼 Arbow 2007-02-13  
优化是编译器/解析器来做的,但是这种风格的代码需要自己来写。

貌似python没有做这优化处理,计算10000就出问题了:
print "Fibonacci(10000)=", fib(10000)
print "Total time:", (time.time()*1000-t*1000)


就会出错了
引用
...
  File "Fib.py", line 9, in tailRecursionFib
    return tailRecursionFib(n-1, f1+f2, f1)
  File "Fib.py", line 9, in tailRecursionFib
    return tailRecursionFib(n-1, f1+f2, f1)
RuntimeError: maximum recursion depth exceeded


栈溢出
http://www.iteye.com/t/17585.html

应该是没有优化了
2 楼 simohayha 2007-02-13  
那么python 中可是实现这种优化吗?

那么 象 FP语言 中的尾递归优化 是自动的?还是说要你自己实现?

ps: 论坛现在的搜索好不稳定,经常就什么都搜索不出来了.
1 楼 Arbow 2007-02-13  
是指“尾递归优化”吧,比如下面的程序

def fib(n):
	if(n==0):
		return 0;
	return tailRecursionFib(n,1,0)

def tailRecursionFib(n, f1, f2):
	if(n==1):
		return f1
	return tailRecursionFib(n-1, f1+f2, f1)

import time
t = time.time()
print "Fibonacci(700)=", fib(700)
print "Total time:", (time.time()*1000-t*1000)


return tailRecursionFib(n-1, f1+f2, f1) 这行递归调用,可以直接优化为跳转指令而不是函数调用(需要保存堆栈),这样速度会快很多,接近于迭代。以前T1讨论过的,很多FP语言都支持这种优化。


e.找到了,见 http://www.iteye.com/post/32289 的最下面

相关推荐

    Python实现tail -f功能

    在Python中实现这一功能,主要有以下几个知识点需要掌握: 1. 文件监控机制:在Unix/Linux系统中,可以利用inotify机制来监控文件系统的事件,如文件被打开、修改、删除等。inotify机制通过内核模块,提供了一种...

    File-Tail-0.99.3.tar.gz

    通过阅读这些源代码,开发者可以了解File-Tail如何实现文件监控,包括文件句柄管理、信号处理、内存管理等方面的知识。 在实际应用中,File-Tail-0.99.3可以广泛应用于系统监控、网络日志分析等领域。例如,在Web...

    window系统 tail -f 功能 界面操作

    实现window系统下 类似 Linux 命令行 tail -f 功能.使用C#语言开发,占用资源小。如有问题可留言

    Python库 | tail-tools-0.19.tar.gz

    资源分类:Python库 所属语言:Python 资源全名:tail-tools-0.19.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059

    tail -f for windows 32/64

    然而,通过在给定的压缩包中提取 `tail.exe` 文件并将其放置在 `C:\Windows\System32` 目录下,我们可以为 Windows 创建类似的功能。 `tail` 命令的基本用途是显示文件的末尾几行。默认情况下,它会显示文件的最后...

    DOS下可以使用的tail -f 工具

    在传统的DOS操作系统环境下,与Unix/Linux系统中的`tail`命令类似的功能,可以通过特定的工具实现。`tail`命令在Unix/Linux中常用于查看文件的末尾内容,特别是跟踪文件的实时更新,这对于日志监控或者调试过程非常...

    python实现tail -f 功能

    tailf与tail -f类似:当文件不增长时并不访问文件 tail -f:只跟踪文件内容 tail -F:文件内容与文件名都跟踪 这篇文章最初是因为reboot的群里,有人去面试,笔试题有这个题,不知道怎么做,什么思路,就发群里大家...

    File-Tail-Scribe

    **File-Tail-Scribe** 是一个专用于收集和处理日志文件的工具模块,它在IT行业中被广泛用于监控和分析系统、应用或服务产生的日志数据。在现代的运维和开发环境中,日志文件是排查问题、追踪系统行为、进行性能优化...

    java实现tail功能

    Java作为一种广泛使用的编程语言,也可以实现类似`tail`的功能。本篇文章将深入探讨如何使用Java来实现`tail`命令的核心功能,即查看指定文件的最后N行。 首先,我们需要理解`tail`命令的基本工作原理。`tail`通常...

    Python库 | tail_recursion-1.1.1-py3-none-any.whl

    在Python中,虽然标准解释器并未默认开启尾递归优化,但一些编译器如Jython和PyPy则支持这一特性。 tail_recursion库的目的是帮助Python程序员实现尾递归优化,即使在标准的CPython环境下也能尽可能地减少递归带来...

    Python编程实现tail-n查看日志文件的方法

    ### Python编程实现tail-n查看日志文件的方法 在日常的软件开发与运维工作中,经常会遇到需要查看日志文件的需求,尤其是对于大型应用的日志管理。传统的`tail -n`命令可以很方便地帮助我们查看文件的最后几行内容...

    我使用过的Linux命令之tailf - 跟踪日志文件/更好的tail -f版本

    在Linux系统中,`tail`命令是一个非常常用的工具,它用于查看文件的末尾部分,通常用于监控日志文件的变化。然而,对于实时监控动态更新的日志文件,`tail -f`选项则更为实用。本篇文章将深入探讨`tail -f`的功能...

    babel-plugin-tailcall-optimization:JavaScript的尾调用优化!

    babel-plugin-tailcall优化 JavaScript的尾调用优化!安装npm install babel-plugin-tailcall-optimization --save-dev 并添加到您的.babelrc : "plugins" : [ "tailcall-optimization" ] 如果您使用babel @ 6,请...

    tail-for-windows.zip

    首先,`tail.exe`是主要的执行程序,它是Windows下的`tail`工具,用户只需将这个文件解压到系统盘下,然后在命令行界面中运行,就可以实时查看指定日志文件的尾部内容。这极大地提升了Windows用户在处理日志文件时的...

    linux nohup及tail-f用法

    在启动WebLogic服务器或其他类似服务时,使用`nohup`和`tail -f`的组合可以实现以下流程: 1. 使用`nohup`启动WebLogic服务器: ```bash nohup ./startWeblogic & ``` 这样,即使你关闭终端,WebLogic服务器也会...

    windows下可用的tail工具,看日志首选,win7/win10可用。

    首先,你需要将`tail.exe`文件复制到`C:\Windows\System32`目录下,因为这是Windows系统路径的一部分,这样可以在任意命令提示符窗口中直接调用`tail`命令。然后,通过命令行(CMD或PowerShell)输入`tail -f xxx....

    k8stail:Kubernetes Pod的tail -f体验

    k8stail使您可以实时监视指定名称空间中的所有pod的日志流或标签,如tail -f 。 目录 选项 发展 作者 执照 要求 Kubernetes 1.3或以上 安装 使用自制软件(仅适用于OS X) 公式可从dtan4 / homebrew-dtan4获得...

    windows下的tail备份

    tail for Windows 是 Windows 下的 tail DOS 命令(类似 UNIX/Linux 下的相同命令)。可用来显示文件尾部内容以及跟踪/监控文件内容变化。你也可以借助重定向符号(&gt; 或 &gt;&gt;)从一个文件的指定行号开始截取内容生成另...

Global site tag (gtag.js) - Google Analytics