홈>
F #에서와 같은 간단한 추가 기능 :
let rec app s t =
match s with
| [] -> t
| (x::ss) -> x :: (app ss t)
함수가 꼬리 재귀가 아니기 때문에 s가 커지면 충돌합니다. F #의 표준 추가 기능이 큰 목록과 충돌하지 않으므로 다르게 구현해야합니다. 그래서 나는 궁금해했다 : 꼬리 재귀 정의 추가는 어떻게 생겼습니까? 나는 다음과 같은 것을 생각해 냈다 :
let rec comb s t =
match s with
| [] -> t
| (x::ss) -> comb ss (x::t)
let app2 s t = comb (List.rev s) t
작동하지만 다소 이상해 보입니다. 더 우아한 정의가 있습니까?
- 답변 # 1
- 답변 # 2
줄리엣이 게시 한 것 외에 :
시퀀스 표현식 사용
내부적으로 시퀀스 표현식은 꼬리 재귀 코드를 생성하므로 제대로 작동합니다.let append xs ys = [ yield! xs yield! ys ]
변경 가능한 .NET 유형 사용
David는 F # 목록이 변경 될 수 있다고 언급했지만 F # 코어 라이브러리로만 제한됩니다 (기능 개념을 위반하므로 사용자가 기능을 사용할 수 없음). 변경 가능한 .NET 데이터 유형을 사용하여 변경 기반 버전을 구현할 수 있습니다.let append (xs:'a[]) (ys:'a[]) = let ra = new ResizeArray<_>(xs) for y in ys do ra.Add(y) ra |> List.ofSeq
일부 시나리오에서는 유용 할 수 있지만 일반적으로 F # 코드에서 돌연변이는 피합니다.
- 답변 # 3
F # 소스를 한 눈에 보면 꼬리가 내부적으로 변경 가능한 것 같습니다. 간단한 해결책은 요소를 두 번째 목록에 맞추기 전에 첫 번째 목록을 뒤집는 것입니다. 그것은 목록을 뒤집는 것과 함께 꼬리를 재귀 적으로 구현하는 것이 쉽지 않습니다.
전통적 (꼬리 재귀 아님)
어큐뮬레이터 사용 (테일 재귀)
계속 (꼬리 재귀)
비 테일 재귀 함수를 연속적으로 재귀로 변환하는 것은 매우 간단하지만 개인적으로는 간단한 가독성을 위해 누산기를 선호합니다.