2009-04-30 4 views
15

내가 follwing을 함수를 쓴 경우 어떻게 알 수 있습니까 :어떻게 함수 F 번호에 꼬리 재귀가

let str2lst str = 
    let rec f s acc = 
     match s with 
     | "" -> acc 
     | _ -> f (s.Substring 1) (s.[0]::acc) 
    f str [] 

은 F # 컴파일러는 루프로 설정되어 있다면 알 수 있습니까? 리플렉터를 사용하지 않고 알아낼 수있는 방법이 있습니까 (리플렉터에 대한 경험이 없으며 C#을 모르겠습니다)?

편집 : 또한 내부 함수를 사용하지 않고 꼬리 재귀 함수를 작성할 수 있습니까? 아니면 루프가 상주해야합니까?

또한 F # std lib에 주어진 함수를 여러 번 실행하여 매번 마지막 출력을 입력으로 제공하는 함수가 있습니까? 문자열을 가지고 있다고 가정 해 봅시다. 문자열 위에 함수를 실행하고 그 결과 문자열을 다시 실행하려고합니다.

+0

또한보십시오 http://stackoverflow.com/questions/5809683/is-my-rec-function-tail-reursive – Brian

답변

22

불행히도 사소한 방법은 없습니다.

소스 코드를 읽고 유형을 사용하고 검사로 꼬리 호출 ('마지막'일 뿐이며 'try'블록이 아님)이 있는지 여부를 확인하는 것이 그리 어렵지는 않지만 두 번째 사용자 - 스스로를 추측하고 실수를 범하십시오. 예를 들어 생성 된 코드를 검사하는 것 이외의 간단한 자동화 된 방법은 없습니다.

물론 테스트 데이터의 큰 부분에 기능을 시험해보고 폭발 여부를 확인할 수 있습니다.

F # 컴파일러는 모든 꼬리 호출에 대해 .tail IL 명령어를 생성합니다 (컴파일러 플래그를 끄지 않는 한 - 디버깅을 위해 스택 프레임을 유지하려는 경우 사용). 단 꼬리 재귀 호출 함수는 루프에 최적화됩니다. (편집 : 요즘 F # 컴파일러는이 호출 사이트를 통해 재귀 루프가 없음을 증명할 수있는 경우 .tail을 내 보내지 못한다고 생각합니다.이 최적화는 .tail opcode가 많은 플랫폼에서 조금 느립니다.)

'tailcall'은 F #의 향후 버전에서 예를 들어 글을 쓸 수 있도록하는 예약 키워드입니다.

tailcall func args 

다음 꼬리 전화가 아닌 경우 경고/오류가 표시됩니다.

자연스럽게 꼬리 재귀 적이 지 않은 함수들 (따라서 여분의 누적 기 매개 변수가 필요함)만이 '내부 함수'관용구에 '강제'할 것입니다. 나는 폴 그레이엄이 리스프에 에 수립 엄지 손가락의 규칙을 좋아

let rec nTimes n f x = 
    if n = 0 then 
     x 
    else 
     nTimes (n-1) f (f x) 

let r = nTimes 3 (fun s -> s^" is a rose") "A rose" 
printfn "%s" r 
+0

"tailcall"은 흥미로운 것입니다. 전체 함수에서 더 유용한 "tailrec"선언. "부분적으로 꼬리 재귀"기능을 사용할 수 있습니까? –

+3

@StephenSwensen 당신은 절대적으로 할 수 있습니다. 제어 흐름 분기는 꼬리 호출과 꼬리 호출이 아닌 호출 사이의 대안으로 이어질 수 있으며 컴파일 타임에 실제로 꼬리 호출 인 것을 확인하기 만하면됩니다. ''rec recf x = if x> 0 then f (1 + x) else 0 + f (1 + x)'는 개념을 설명합니다. –

2

: 여기

는 코드 당신이 무엇을 요구 샘플의 작업, 예를을 왼쪽으로가있는 경우 재귀 호출 출력을 조작하면 호출은 꼬리 재귀가 아닙니다.