trampoline

3866 단어
파이톤의 귀환은 최대 제한이 있습니다. 이 제한을 초과하면 던집니다.
RuntimeError: maximum recursion depth exceeded
sys.getrecursionlimit() 귀환의 최대 층수를 볼 수 있습니다. 기본값은 1000입니다.sys.setrecursionlimit() 이 최대 층수를 바꿀 수 있습니다.
cPython은 끝부분 최적화를 지원하지 않지만, 예를 들어trampoline와 같은 신기한 기술을 빙빙 돌릴 수 있는 hack의 방법도 있다.귀속 함수를 호출할 때, 우선trampoline 함수를 삽입합니다.이 함수를 통해 진정한 귀속 함수를 호출하고 이를 수정하여 다시 귀속 함수를 호출하지 않고 다음 귀속 호출의 매개 변수를 직접 되돌려줍니다.trampoline에서 다음 귀속 호출을 진행합니다. 이렇게 한 층 한 층 귀속 호출은 한 번 반복 호출이 됩니다.
개인적으로 만약에 이런 hack 방법을 사용하려면 꼬리 귀속을 최적화할 수 있는 언어를 사용하는 것이 낫다고 생각한다
참조http://code.activestate.com/recipes/474088/약간의 변동이 있다
#!/usr/bin/env python2.7
# 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
from functools import wraps


class TailRecurseException(BaseException):

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


def tail_call_optimized(func):
    """
    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.
    """
    @wraps(func)
    def wrapper(*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 True:
                try:
                    return func(*args, **kwargs)
                except TailRecurseException as e:
                    args = e.args
                    kwargs = e.kwargs
    return wrapper


@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.

이 코드의 가장 흥미로운 점은 귀속이 발생하면 함수 파라미터를 가진 사용자 정의 이상을 던지고 순환하는 방식으로 다시 호출하는 것이다.이렇게 하면 우리가 코드를 쓸 때 쓰는 것은 귀속이지만 실제로 발생하는 교체이다.
위의 코드는 sys._getframe() 창고 프레임을 가져와서 귀속 여부를 판단합니다.이것은 평소에 코드를 쓸 때 일반적으로 거의 접촉하지 않는다.
파이썬 문서에서 이렇게 말했어요.
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.
CPython implementation detail: This function should be used for internal and specialized purposes only. It is not guaranteed to exist in all implementations of Python.
#  
f = sys._getframe()
#  
f.f_back
#  
f.f_code
#  
f.f_code.co_name

또한 이전 스택 프레임이 없는 경우 f.f백이 None으로 돌아갑니다.
이전 코드로 돌아가서 프로세스 실행factorial(1000)을 대체적으로 분석해 봅시다.tail 을 사용했기 때문입니다.call_optimized 장식기, 실제로 우리는 wrapper를 호출했다. 이 때 이전 창고 프레임의 코드 대상 이름은 이고, 앞 창고 프레임의 코드 대상 이름은 execfile while True 사순환에 들어가고, 실행 func(*args, **kwargs) 은 새로운 창고 프레임을 만들었다.이 때 컴포지팅 호출이 발생하고 새로운 wrapper 창고 프레임에 들어가지만 창고 프레임에 대응하는 코드 대상(f code)이 변하지 않기 때문에 사용자 정의 이상TailRecurseException을 던져서 이 이상을 포획한 후 이상 이미지에서 읽은 파라미터로 다시 실행func(*args, **kwargs)주의해라, 우리는 창고 프레임의 선형 증가를 막지 않았다.
그리고 일드를 이용한 실현도 있어요.
http://www.jianshu.com/p/d36746ad845d

좋은 웹페이지 즐겨찾기