Có phải đệ quy là một ví dụ của việc quá thông minh khi lập trình?


8

Tôi đã đọc một vài cuốn sách và học hỏi qua kinh nghiệm rằng tối ưu hóa mã đến mức không thể hiểu được hoặc đưa ra giải pháp cực kỳ nhanh nhưng cực kỳ phức tạp cho một vấn đề không được mong muốn khi làm việc theo nhóm hoặc ngay cả khi bạn làm việc một mình và phải hiểu giải pháp thông minh của bạn một thời gian sau.

Câu hỏi của tôi là, có nên đệ quy theo cách tương tự? Có phải lập trình viên trung bình hiểu đệ quy dễ dàng và do đó người ta nên sử dụng nó với sự không khoan nhượng, hoặc lập trình viên trung bình không hiểu rõ về đệ quy và người ta nên tránh xa nó vì lợi ích chung của năng suất nhóm?

Tôi biết có những câu trả lời đơn giản, "Bất kỳ lập trình viên nào không hiểu đệ quy đều không đáng một hạt muối, vì vậy đừng lo lắng về chúng" nhưng tôi tự hỏi liệu tất cả các bạn có trải nghiệm thực tế nào mà bạn muốn chia sẻ rằng sẽ làm sáng tỏ vấn đề nhiều hơn ý kiến ​​tôi vừa đề cập.


2
Câu hỏi này khá giống với lập trình viên.stackexchange.com/ questions/24997/ đã được hỏi ngày hôm nay. Một số câu trả lời tốt ở đó.
Nicole

4
Làm thế nào bạn có thể trở thành một lập trình viên trung bình nếu bạn không hiểu đệ quy? (lưu ý sự khác biệt so với "bất kỳ lập trình viên nào")

Câu trả lời:


22

Có phải lập trình viên trung bình hiểu đệ quy dễ dàng và do đó người ta nên sử dụng nó với sự không khoan nhượng, hoặc lập trình viên trung bình không hiểu rõ về đệ quy và người ta nên tránh xa nó vì lợi ích chung của năng suất nhóm?

Tôi muốn nói rằng các lập trình viên trung bình hiểu đệ quy một cách hoàn hảo. Thật vậy, nếu lập trình viên đã có bằng về Khoa học Máy tính hoặc Kỹ thuật phần mềm thì nó được đảm bảo khá nhiều. Cấp, có một số lập trình viên dưới mức trung bình ngoài kia, nhưng bạn không muốn họ trong nhóm của bạn.

Trong trường hợp này, sự khác biệt giữa một người lập trình trung bình và một lập trình viên giỏi là biết khi nào nên sử dụng đệ quy và khi nào không. Và điều đó phụ thuộc vào vấn đề đang được giải quyết VÀ ngôn ngữ được sử dụng để giải quyết nó.

  • Nếu bạn đang sử dụng ngôn ngữ lập trình chức năng, đệ quy là một giải pháp tự nhiên và hiệu quả cho một loạt các vấn đề. (Quy tắc tối ưu hóa đệ quy đuôi!)

  • Nếu bạn đang sử dụng OO hoặc ngôn ngữ thủ tục đơn giản, đệ quy có thể không hiệu quả và có thể có vấn đề do tràn ngăn xếp. Vì vậy, trong một số trường hợp, bạn sẽ chọn một giải pháp lặp chứ không phải là một giải pháp đệ quy. Tuy nhiên, trong các trường hợp khác, giải pháp đệ quy đơn giản và thanh lịch hơn nhiều đến nỗi giải pháp lặp (có thể hiệu quả hơn) sẽ là giải pháp "quá thông minh". (Ví dụ: nếu sự cố yêu cầu quay lại, xây dựng hoặc đi bộ cây / đồ thị, v.v., đệ quy thường đơn giản hơn.)


1
+1 Đối với nhận xét đệ quy đuôi. Thông thường, một nỗ lực để loại bỏ đệ quy kết thúc với rất nhiều cấu trúc ngăn xếp và các vòng lặp trong mã của bạn, giúp sao chép hiệu quả thành phần ngăn xếp cuộc gọi.
Orble

1
@ Đơn giản - đó là sự thật, nhưng một ngăn xếp được triển khai bằng cách sử dụng một mảng hoặc danh sách mảng có khả năng mở rộng hơn so với ngăn xếp cuộc gọi của một luồng. Đặc biệt là các ngăn xếp cuộc gọi luồng không thể phát triển trong nhiều triển khai ngôn ngữ.
Stephen C

Đúng vậy, nó là một vấn đề muôn thuở của thiết kế hàm đệ quy. Một vấn đề cho các nhà thiết kế ngôn ngữ để giải quyết hơn là lập trình viên, lý tưởng.
Orble

" Cấp, có một số lập trình viên dưới mức trung bình ngoài kia ": OK, phải nói rằng ... Theo định nghĩa rất rõ, một nửa số lập trình viên ngoài kia có dưới trung bình và một nửa trong số đó là "rất thấp" trung bình.
Lawrence Dol

@LawrenceDol - Tôi chưa bao giờ thấy ai đó tuyên bố rằng "rất" có nghĩa là một nửa của bất cứ điều gì. Nguồn của bạn cho định nghĩa của bạn là gì? (Điều này cũng phải được hỏi, bởi vì nhà sư phạm chỉ có bất kỳ giá trị nào nếu nó thực sự dựa trên.)
Stephen C

42

Một số vấn đề được đệ quy tự nhiên. Đến với một giải pháp lặp đi lặp lại trong những trường hợp này thực sự có thể trở nên cồng kềnh và phức tạp hơn so với các giải pháp đệ quy. Một ví dụ điển hình là bất kỳ thuật toán nào cần duyệt qua cấu trúc cây phân cấp, đây là một nhiệm vụ không phổ biến trong lập trình.

Phiên bản TL; DR: Không.


9
Đã đồng ý. Nguy hiểm hơn khi gắn nhãn những thứ là "có hại" và đi đến cực đoan để tránh chúng - điều sẽ luôn luôn gây nhầm lẫn và khó duy trì hơn về phía trước.
heretik

11

Đệ quy là một nguyên tắc nền tảng trong hầu hết các ngôn ngữ lập trình chức năng. Lặp lại (lặp) trong các ngôn ngữ chức năng thường được thực hiện thông qua đệ quy.

Các ngôn ngữ chức năng đã thấy phần nào sự phục hưng gần đây, do nhu cầu xử lý thanh lịch nhiều lõi xử lý hơn; ngôn ngữ chức năng giúp đạt được loại đồng thời này bằng cách cung cấp các cách để lý do tốt hơn về chương trình của bạn mà không có sự phức tạp liên quan đến việc khóa các cấu trúc có thể thay đổi.


+1 Nhận xét tốt. Mặc dù tôi không chắc chắn "sự phục hưng" là hoàn toàn đúng, hay lý do của đa lõi. Họ luôn là một mô hình yêu thích của những người biết về họ, nhưng sự phổ biến của họ trong dòng chính là không bao giờ tuyệt vời. Các ngôn ngữ chức năng đã trở nên chính thống hơn, sẽ mất toàn bộ chủ đề để vượt qua vấn đề đó. ( lập trình
viên.stackexchange.com/questions/20036 / Google

11

Một số vấn đề, chẳng hạn như đi bộ một cấu trúc cây (đi bộ, giả sử, toàn bộ cấu trúc thư mục, trái ngược với, tìm kiếm một nút B-Tree cụ thể), rất phù hợp để sử dụng đệ quy; tương đương không đệ quy thường chỉ đơn giản là thêm sự phức tạp của việc quản lý ngăn xếp của riêng bạn.

Trong những trường hợp này, đệ quy là giải pháp tốt nhất, đơn giản nhất và dễ hiểu nhất.


7

Tôi sẽ nói sử dụng đệ quy khi nó phù hợp, nhưng luôn tìm cách để tránh đệ quy rõ ràng.

Ví dụ, nếu bạn thấy cần phải duyệt thủ công một cấu trúc cây, thì có, bạn nên sử dụng đệ quy. Nếu ai đó đủ câm để không hiểu được một cây đệ quy, họ sẽ không tạo ra đầu hay đuôi của phiên bản lặp.

Nhưng một lựa chọn tốt hơn là sử dụng một trình vòng lặp trừu tượng hóa đệ quy đến điểm mà tất cả những gì bạn thấy là "Tôi thực hiện thao tác này trên mọi thứ trên cây".


1
Đúng (và +1), nhưng, tất nhiên, ai đó phải viết trừu tượng.
Lawrence Dol

1
Như @Software Monkey nói, thông thường vẫn nên được mã hóa theo cách đệ quy, nhưng trừu tượng hóa với một trình vòng lặp để "sử dụng" là tốt.
Orble

1
+1 cho điểm mà một người không hiểu phiên bản đệ quy rất có thể sẽ không hiểu phiên bản lặp. Thật không may, người đó cũng sẽ không nhận ra rằng anh ta không hiểu phiên bản lặp.
Larry Coleman

3

Đệ quy, là cơ chế đơn giản nhất theo quan điểm về độ sạch của mã. Nếu tốc độ hoàn toàn là điều cốt yếu, thì có lẽ bạn có thể sử dụng một mảng 2D các tham số vấn đề. Sinh ra một quá trình con gái sau đó chỉ đơn giản là nối thêm một mục khác vào mảng. Tôi đã từng làm một trình giải mã trình biên dịch hợp ngữ, đó là một vấn đề lớn trong ngày. Ngữ cảnh cần cho mỗi phiên bản là 8 từ cho mỗi cấp độ và mỗi bài toán con là một phần ba kích thước của phần trước. "Thư viện" này chỉ phổ biến vì nó đánh bại tất cả các triển khai khác ngoài kia. Nhưng, đó là một tình huống khá hiếm gặp trong lập trình, vì vậy bạn không cần phải chịu thua "tối ưu hóa sớm", chỉ vì giải pháp này đã có sẵn. Rõ ràng đối với một vài điều, sự quá mức khủng khiếp của nó, như ví dụ 101 đệ quy "tính giai thừa". Nhưng đối với hầu hết các ứng dụng,

Tôi có một trình kiểm tra chính tả đơn giản mà tôi sử dụng cho một ứng dụng, (nơi tôi muốn đưa ra gợi ý về cách sửa lỗi chính tả nhỏ), trong đó tôi tính "khoảng cách" giữa hai chuỗi, cho phép xóa và bổ sung được cho phép. Điều này dẫn đến một cấu trúc cây có khả năng lớn và các nhánh được cắt tỉa vì chúng ta chỉ quan tâm đến các trận đấu gần. Với đệ quy, có thể hai mươi dòng mã (tôi có cả phiên bản Fortran và C). Tôi nghĩ rằng nó sẽ lộn xộn nếu không. Heck nó dễ dàng hơn nhiều để lập trình / gỡ lỗi xác minh, hơn là nghĩ về cây đó!


0

Tôi hiểu nó nhưng tôi chưa bao giờ sử dụng nó một cách rõ ràng. Tôi nghĩ rằng bất cứ ai tự gọi mình là lập trình viên đều nên biết hoặc tìm hiểu nó là gì nhiều như họ cần để biết stack và heap là gì. Này, mọi người cần biết min / max hoạt động như thế nào trên tic-tac-toe, thông qua đệ quy ưa thích.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.