2010-12-12 50 views
9

Tôi đang viết lại một số mã hiện có trong một thiết lập nơi các cuộc gọi đệ quy không dễ triển khai cũng như mong muốn. (Và trong Fortran 77, nếu bạn phải biết.) Tôi đã nghĩ về việc tạo một ngăn xếp từ đầu để theo dõi các cuộc gọi cần thiết, nhưng điều này có vẻ kludgy, và tôi không muốn cấp phát bộ nhớ cho một mảng trong trường hợp đệ quy không sâu. (Tôi không tự tin rằng Fortran 77 cũng hỗ trợ kích thước mảng động.)Viết lại một hàm đệ quy mà không sử dụng đệ quy

Bất kỳ đề xuất nào khác về cách thực hiện hàm đệ qui rõ ràng và viết lại nó không đệ quy mà không lãng phí không gian trên ngăn xếp?

Rất cám ơn, Cũ MCST

+2

Nếu nó không nhánh bạn thường có thể viết lại nó thành một vòng lặp đơn giản. Nếu nó chi nhánh bạn thường cần một ngăn xếp – CodesInChaos

+1

@CodeInChaos: một hàm đệ quy mà không chi nhánh sẽ, theo định nghĩa, không bao giờ trở lại ... –

+0

Đoán tôi sử dụng sai từ chi nhánh. Tôi có nghĩa là cuộc gọi chính nó nhiều lần, do đó, đồ thị của các cuộc gọi trở thành một cây với các chi nhánh. Và đó chỉ là kinh nghiệm của tôi và không phải lúc nào cũng đúng. – CodesInChaos

Trả lời

14

Nếu mã của bạn sử dụng đệ quy đuôi (có nghĩa là, hàm trả về kết quả của mỗi cuộc gọi đệ quy trực tiếp mà không cần bất kỳ chế biến khác) sau đó nó có thể viết lại chức năng phải nhất thiết mà không cần một chồng:

function dosomething(a,b,c,d) 
{ 
    // manipulate a,b,c,d 
    if (condition) 
    return dosomething(a,b,c,d) 
    else 
    return something; 
} 

Into:

function dosomething(a,b,c,d) 
{ 
    while (true) 
    { 
    // manipulate a,b,c,d 
    if (condition) continue; 
    else return something; 
    } 
} 

Không cần đệ quy đuôi, sử dụng ngăn xếp (hoặc lưu trữ trung gian tương tự) là giải pháp duy nhất.

+0

Điều này tương tự như những gì tôi đang suy nghĩ, nhưng không thể nói thành lời. Vì vậy, trong trường hợp cụ thể của tôi, tôi có một tập dữ liệu của các yếu tố mà mỗi sẽ cần phải được kiểm tra cho một mối quan hệ với các yếu tố khác trong tập hợp. Tôi có thể vượt qua trong cấu trúc dữ liệu của tất cả các mục, nhưng vì mỗi nhu cầu xử lý tiếp tục, tôi cho rằng ngăn xếp là không thể tránh khỏi, phải không? –

+0

Tùy thuộc.Nếu mã chủ yếu là "cho tất cả các cặp (a, b) nếu P (a, b) là true thì thực thi F (a, b)" bạn có thể lấy đi với lặp vòng lặp qua tất cả các cặp ... –

2

Hầu hết các chức năng đệ quy có thể dễ dàng viết lại như vòng, như để lãng phí thời gian - điều đó phụ thuộc vào chức năng, vì nhiều (nhưng không phải tất cả) đệ quy thuật toán thực sự phụ thuộc vào loại đó lưu trữ (mặc dù, phiên bản vòng lặp thường là hiệu quả hơn trong những trường hợp này quá).

+0

Chăm sóc để hiển thị chức năng đệ quy không yêu cầu bất kỳ không gian nào trên ngăn xếp? Ngay cả đối với các đối số của nó? –

+1

@Nikita Rybak: một hàm đệ quy đuôi nội tuyến;) –

+0

@Victor Vâng, nhưng đó là dạng viết lại. Và Ofir tuyên bố có một hàm đệ quy không yêu cầu bộ nhớ ngăn xếp. Vì vậy, tôi tò mò. –

3

Chức năng đệ quy cổ điển có thể được viết như một vòng lặp là hàm Fibonacci:

function fib(n) 
{ 
    // valid for n >= 0 
    if (n < 2) 
     return n; 
    else 
     return fib(n-1) + fib(n-2); 
} 

Nhưng mà không memoization này mất O (exp^N) hoạt động với O (N) ngăn xếp không gian.

Nó có thể được viết lại:

function fib(n) 
{ 
    if (n < 2) 
     return n; 
    var a = 0, b = 1; 
    while (n > 1) 
    { 
     var tmp = a; 
     a = b; 
     b = b + tmp; 
     n = n - 1; 
    } 
    return b; 
} 

Nhưng điều này liên quan đến kiến ​​thức về cách các chức năng hoạt động, không chắc chắn nếu nó có thể được khái quát hóa một quá trình tự động.