Điều gì là quá khó khăn về con trỏ / đệ quy? [đóng cửa]


20

Trong những nguy hiểm của các trường java, Joel thảo luận về kinh nghiệm của anh ấy tại Penn và sự khó khăn của "lỗi phân khúc". Anh ta nói

[segfaults rất khó cho đến khi bạn] "hít một hơi thật sâu và thực sự cố gắng buộc tâm trí của bạn phải làm việc ở hai mức độ trừu tượng khác nhau cùng một lúc."

Đưa ra một danh sách các nguyên nhân phổ biến cho segfaults, tôi không hiểu làm thế nào chúng ta phải làm việc ở 2 mức độ trừu tượng.

Vì một số lý do, Joel coi những khái niệm này là cốt lõi để một lập trình viên có khả năng trừu tượng hóa. Tôi không muốn giả định quá nhiều. Vì vậy, những gì là khó khăn về con trỏ / đệ quy? Ví dụ sẽ tốt đẹp.


31
Đừng lo lắng về những gì Joel có thể nghĩ về bạn. Nếu bạn thấy đệ quy dễ dàng, điều đó tốt. Không phải ai khác cũng vậy.
Thất vọngWithFormsDesigner

6
Đệ quy là dễ dàng theo định nghĩa (chức năng gọi là tự), nhưng biết khi nào nên sử dụng nó và làm thế nào để làm cho nó hoạt động là phần khó khăn.
JeffO

9
Áp dụng cho một công việc tại Fog Creek và cho chúng tôi biết làm thế nào nó đi. Chúng tôi đều rất quan tâm đến việc tự quảng cáo của bạn.
Joel Etherton

4
@ P.Brian.Mackey: Chúng tôi không hiểu lầm. Câu hỏi không thực sự hỏi bất cứ điều gì. Đó là tự quảng cáo trắng trợn. Nếu bạn muốn biết Joel đang hỏi gì về con trỏ / đệ quy, hãy hỏi anh ấy: team@stackoverflow.com
Joel Etherton

19
Bản sao của câu hỏi này ?
ozz

Câu trả lời:


38

Đầu tiên tôi nhận thấy rằng con trỏ và đệ quy là khó khăn trong trường đại học. Tôi đã tham gia một vài khóa học năm đầu tiên điển hình (một là C và Assembler, hai là ở Scheme). Cả hai khóa học bắt đầu với hàng trăm sinh viên, nhiều người trong số họ có nhiều năm kinh nghiệm lập trình cấp trung học (điển hình là BASIC và Pascal, trong những ngày đó). Nhưng ngay khi con trỏ được giới thiệu trong khóa C, và đệ quy được giới thiệu trong khóa Scheme, một số lượng lớn sinh viên - có lẽ thậm chí là đa số - đã hoàn toàn thất vọng. Đây là những đứa trẻ đã viết RẤT NHIỀU mã trước đây và không có vấn đề gì, nhưng khi chúng đánh con trỏ và đệ quy, chúng cũng đánh vào tường về khả năng nhận thức của chúng.

Giả thuyết của tôi là con trỏ và đệ quy giống nhau ở chỗ chúng yêu cầu bạn giữ hai mức độ trừu tượng trong đầu cùng một lúc. Có một cái gì đó về sự trừu tượng nhiều cấp độ đòi hỏi một loại năng khiếu tinh thần mà rất có thể một số người sẽ không bao giờ có.

  • Với con trỏ, "hai mức độ trừu tượng" là "dữ liệu, địa chỉ dữ liệu, địa chỉ địa chỉ dữ liệu, v.v." hoặc theo cách chúng ta thường gọi là "giá trị so với tham chiếu". Đối với học sinh chưa được đào tạo, rất khó để thấy sự khác biệt giữa địa chỉ của xx .
  • Với đệ quy, "hai mức độ trừu tượng" đang hiểu làm thế nào để một hàm có thể tự gọi chính nó. Thuật toán đệ quy đôi khi là thứ mà mọi người gọi là "lập trình bằng suy nghĩ mơ ước" và việc nghĩ ra thuật toán theo "trường hợp cơ bản + trường hợp quy nạp" thay vì danh sách các bước tự nhiên hơn mà bạn thực hiện để giải quyết vấn đề là rất tự nhiên. . " Đối với học sinh chưa được huấn luyện nhìn vào một thuật toán đệ quy, thuật toán xuất hiện để đặt câu hỏi .

Tôi cũng sẽ hoàn toàn sẵn sàng chấp nhận rằng có thể dạy con trỏ và / hoặc đệ quy cho bất cứ ai ... Tôi không có bất kỳ bằng chứng nào bằng cách này hay cách khác. Tôi biết rằng theo kinh nghiệm, việc có thể thực sự hiểu hai khái niệm này là một yếu tố dự báo rất tốt về khả năng lập trình chung và trong quá trình đào tạo CS đại học thông thường, hai khái niệm này là một trong những trở ngại lớn nhất.


4
"Rất, rất không tự nhiên khi nghĩ về một thuật toán theo" trường hợp cơ sở + trường hợp quy nạp "" - Tôi nghĩ rằng không có gì là không tự nhiên, chỉ là trẻ em không được đào tạo phù hợp.
Ingo

14
nếu đó là tự nhiên, bạn sẽ không cần phải được đào tạo. : P
Joel Spolsky

1
Điểm hay :), nhưng chúng ta không cần đào tạo về toán, logic, vật lý, v.v. tất cả đều theo nghĩa rộng hơn là tự nhiên nhất. Thật thú vị, ít lập trình viên có bất kỳ vấn đề với cú pháp của ngôn ngữ, nhưng nó đầy đệ quy.
Ingo

1
Tại trường đại học của tôi, khóa học đầu tiên bắt đầu với lập trình chức năng và đệ quy gần như ngay lập tức, trước khi giới thiệu đột biến và tương tự. Tôi thấy rằng một số sinh viên không có kinh nghiệm hiểu đệ quy tốt hơn những người có một số kinh nghiệm. Điều đó nói rằng, đầu lớp được tạo thành từ những người có nhiều kinh nghiệm.
Tikhon Jelvis

2
Tôi nghĩ rằng việc không thể hiểu được con trỏ và đệ quy có liên quan đến a) mức IQ tổng thể và b) giáo dục toán học tồi.
quant_dev

23

Đệ quy không chỉ là "một hàm gọi chính nó." Bạn sẽ không thực sự đánh giá cao tại sao việc đệ quy lại khó khăn cho đến khi bạn thấy mình vẽ ra các khung stack để tìm ra điều gì sai với trình phân tích cú pháp gốc đệ quy của bạn. Thông thường bạn sẽ có các hàm đệ quy lẫn nhau (hàm A gọi hàm B, gọi hàm C, có thể gọi hàm A). Có thể rất khó để tìm ra điều gì đã sai khi bạn N stackframes nằm sâu trong một chuỗi các hàm đệ quy lẫn nhau.

Đối với con trỏ, một lần nữa, khái niệm con trỏ khá đơn giản: một biến lưu trữ một địa chỉ bộ nhớ. Nhưng một lần nữa, khi có sự cố xảy ra với cấu trúc dữ liệu phức tạp của void**con trỏ trỏ đến các nút khác nhau, bạn sẽ thấy lý do tại sao nó có thể trở nên khó khăn khi bạn đấu tranh để tìm ra lý do tại sao một trong những con trỏ của bạn lại trỏ đến một địa chỉ rác.


1
Việc thực hiện một trình phân tích cú pháp tốt đệ quy là khi tôi thực sự cảm thấy mình có phần nào hiểu được đệ quy. Con trỏ rất dễ hiểu ở mức cao như bạn đã nói; chỉ đến khi bạn tham gia vào các loại hạt và bu lông của việc triển khai xử lý các con trỏ mà bạn thấy tại sao chúng phức tạp.
Chris

Đệ quy lẫn nhau giữa nhiều chức năng về cơ bản là giống như goto.
starblue

2
@starblue, không thực sự - vì mỗi stackframe tạo ra các phiên bản mới của các biến cục bộ.
Charles Salvia

Bạn nói đúng, chỉ đệ quy đuôi là giống như goto.
starblue

3
@wnoise int a() { return b(); }có thể được đệ quy, nhưng nó phụ thuộc vào định nghĩa của b. Vì vậy, nó không đơn giản như nó có vẻ ...
thay thế vào

14

Java hỗ trợ con trỏ (chúng được gọi là tài liệu tham khảo) và nó hỗ trợ đệ quy. Vì vậy, trên bề mặt, lập luận của ông xuất hiện vô nghĩa.

Những gì anh ấy thực sự nói về là khả năng gỡ lỗi. Một con trỏ Java (err, Reference) được đảm bảo để trỏ đến một đối tượng hợp lệ. Con trỏ AC không. Và mẹo trong lập trình C, giả sử rằng bạn không sử dụng các công cụ như valgrind , là tìm ra chính xác nơi bạn vặn con trỏ (hiếm khi tại điểm được tìm thấy trong stacktrace).


5
Con trỏ trên mỗi se là một chi tiết. Sử dụng các tham chiếu trong Java không phức tạp hơn việc sử dụng các biến cục bộ trong C. Ngay cả việc trộn chúng theo cách triển khai Lisp (một nguyên tử có thể là một số nguyên có kích thước giới hạn hoặc ký tự hoặc con trỏ) không khó. Khó hơn khi ngôn ngữ cho phép cùng một loại dữ liệu là cục bộ hoặc được tham chiếu, với cú pháp khác nhau và thực sự có lông khi ngôn ngữ cho phép số học con trỏ.
David Thornley

@David - ừm, chuyện này có liên quan gì đến câu trả lời của tôi?
Anon

1
Nhận xét của bạn về con trỏ hỗ trợ Java.
David Thornley

"nơi bạn vặn một con trỏ (hiếm khi tại điểm được tìm thấy trong stacktrace)." Nếu bạn đủ may mắn để có được một stacktrace.
Omega Centauri

5
Tôi đồng ý với David Thornley; Java không hỗ trợ các con trỏ trừ khi tôi có thể tạo một con trỏ tới một con trỏ tới một con trỏ tới một con trỏ tới một int. Mà có lẽ tôi cho rằng tôi có thể bằng cách tạo ra 4-5 lớp mà mỗi lớp tham chiếu một cái gì đó khác, nhưng đó có thực sự là con trỏ hay đó là một cách giải quyết xấu xí?
thay thế

12

Vấn đề với con trỏ và đệ quy không phải là chúng khó hiểu, mà là chúng được dạy rất tệ, đặc biệt là đối với các ngôn ngữ như C hoặc C ++ (chủ yếu là vì bản thân các ngôn ngữ đang được dạy kém). Mỗi khi tôi nghe (hoặc đọc) ai đó nói "một mảng chỉ là một con trỏ" tôi chết một chút bên trong.

Tương tự như vậy, mỗi khi ai đó sử dụng hàm Fibonacci để minh họa đệ quy tôi muốn hét lên. Đó là một ví dụ tồi vì phiên bản lặp không khó viết hơn nó hoạt động ít nhất hoặc tốt hơn so với phiên bản đệ quy và nó không cho bạn cái nhìn sâu sắc thực sự về lý do tại sao một giải pháp đệ quy sẽ hữu ích hoặc mong muốn. Quicksort, cây traversal, vv, là xa ví dụ tốt hơn cho lý do tại sao và làm thế nào đệ quy.

Phải làm quen với con trỏ là một yếu tố làm việc trong một ngôn ngữ lập trình phơi bày chúng. Các thế hệ lập trình viên Fortran đang xây dựng danh sách, cây và ngăn xếp và hàng đợi mà không cần loại con trỏ chuyên dụng (hoặc cấp phát bộ nhớ động) và tôi chưa bao giờ nghe ai cáo buộc Fortran là ngôn ngữ đồ chơi.


Tôi sẽ đồng ý, tôi đã có nhiều năm / thập kỷ của Fortran trước khi nhìn thấy con trỏ thực tế, vì vậy tôi đã sử dụng cách riêng của mình để làm điều tương tự, trước khi có cơ hội để cho lanquage / trình biên dịch làm điều đó cho tôi. Tôi cũng nghĩ rằng cú pháp C liên quan đến con trỏ / địa chỉ rất khó hiểu, mặc dù khái niệm về một giá trị, được lưu trữ tại một địa chỉ rất đơn giản.
Omega Centauri

nếu bạn có một liên kết đến Quicksort được triển khai trong Fortran IV, tôi rất muốn thấy nó. Không nói rằng điều đó không thể thực hiện được - thực tế, tôi đã thực hiện nó trong BASIC khoảng 30 năm trước - nhưng tôi rất muốn thấy nó.
Anon

Tôi chưa bao giờ làm việc trong Fortran IV, nhưng tôi đã thực hiện một số thuật toán đệ quy trong triển khai VAX / VMS của Fortran 77 (có một cái móc để cho phép bạn lưu mục tiêu của goto như một loại biến đặc biệt, vì vậy bạn có thể viết GOTO target) . Tôi nghĩ rằng chúng tôi đã phải xây dựng ngăn xếp thời gian chạy của riêng mình, mặc dù. Điều này đã đủ lâu rồi mà tôi không thể nhớ chi tiết nữa.
John Bode

8

Có một số khó khăn với con trỏ:

  1. Aliasing Khả năng thay đổi giá trị của một đối tượng bằng cách sử dụng các tên / biến khác nhau.
  2. Không cục bộ Khả năng thay đổi giá trị đối tượng trong ngữ cảnh khác với bối cảnh được khai báo (điều này cũng xảy ra với các đối số được truyền bằng tham chiếu).
  3. Sự không phù hợp trọn đời Thời gian tồn tại của một con trỏ có thể khác với thời gian tồn tại của đối tượng mà nó trỏ đến và điều đó có thể dẫn đến các tham chiếu không hợp lệ (SEGFAULTS) hoặc rác.
  4. Số học con trỏ . Một số ngôn ngữ lập trình cho phép thao tác con trỏ dưới dạng số nguyên và điều đó có nghĩa là con trỏ có thể trỏ đến bất kỳ đâu (kể cả những nơi không mong đợi nhất khi có lỗi). Để sử dụng số học con trỏ một cách chính xác, một lập trình viên phải nhận thức được kích thước bộ nhớ của các đối tượng được trỏ đến, và đó là điều cần suy nghĩ nhiều hơn.
  5. Kiểu phôi Khả năng truyền con trỏ từ loại này sang loại khác cho phép ghi đè lên bộ nhớ của một đối tượng khác với mục đích.

Đó là lý do tại sao một lập trình viên phải suy nghĩ thấu đáo hơn khi sử dụng con trỏ (tôi không biết về hai mức độ trừu tượng ). Đây là một ví dụ về những sai lầm điển hình được thực hiện bởi một người mới:

Pair* make_pair(int a, int b)
{
    Pair p;
    p.a = a;
    p.b = b;
    return &p;
}

Lưu ý rằng mã như trên là hoàn toàn hợp lý trong các ngôn ngữ không có khái niệm về con trỏ mà chỉ là một trong các tên (tham chiếu), đối tượng và giá trị, như ngôn ngữ lập trình chức năng và ngôn ngữ có bộ sưu tập rác (Java, Python) .

Khó khăn với các hàm đệ quy xảy ra khi những người không có đủ nền tảng toán học (trong đó đệ quy là kiến ​​thức phổ biến và bắt buộc) cố gắng tiếp cận họ nghĩ rằng hàm này sẽ hoạt động khác nhau tùy thuộc vào số lần nó được gọi trước đó . Vấn đề đó trở nên trầm trọng hơn vì các hàm đệ quy thực sự có thể được tạo ra theo cách mà bạn phải nghĩ theo cách đó để hiểu chúng.

Hãy nghĩ về các hàm đệ quy với các con trỏ được truyền xung quanh, giống như trong quá trình thực hiện thủ tục của Cây Đỏ-Đen trong đó cấu trúc dữ liệu được sửa đổi tại chỗ; đó là một cái gì đó khó khăn hơn để suy nghĩ về một đối tác chức năng .

Nó không được đề cập trong câu hỏi, nhưng vấn đề quan trọng khác mà người mới gặp khó khăn là đồng thời .

Như những người khác đã đề cập, có một vấn đề bổ sung, phi khái niệm với một số cấu trúc ngôn ngữ lập trình: đó là ngay cả khi chúng ta hiểu, những lỗi đơn giản và trung thực với các cấu trúc đó có thể cực kỳ khó gỡ lỗi.


Sử dụng hàm đó sẽ trả về một con trỏ hợp lệ nhưng biến nằm trong phạm vi cao hơn phạm vi được gọi là hàm, vì vậy con trỏ có thể (giả sử sẽ) bị vô hiệu khi sử dụng malloc.
đúng

4
@Radek S: Không, nó sẽ không. Nó sẽ trả về một con trỏ không hợp lệ mà trên một số môi trường xảy ra hoạt động trong một thời gian cho đến khi một cái gì đó khác ghi đè lên nó. (Trong thực tế, đây sẽ là ngăn xếp, không phải là đống. malloc()Không có khả năng nhiều hơn bất kỳ chức năng nào khác để làm như vậy.)
wnoise

1
@Radeck Trong hàm mẫu, con trỏ trỏ tới bộ nhớ mà ngôn ngữ lập trình (C trong trường hợp này) đảm bảo sẽ được giải phóng khi hàm trả về. Do đó, con trỏ trả về trỏ đến rác . Các ngôn ngữ với bộ sưu tập rác giữ cho đối tượng tồn tại miễn là nó được tham chiếu trong bất kỳ ngữ cảnh nào.
Apalala

Nhân tiện, Rust có con trỏ nhưng không có những vấn đề này. (khi không ở trong bối cảnh không an toàn)
SUND Borsch

2

Con trỏ và đệ quy là hai con thú riêng biệt và có những lý do khác nhau khiến chúng trở nên "khó khăn".

Nói chung, con trỏ đòi hỏi một mô hình tinh thần khác với việc gán biến thuần túy. Khi tôi có một biến con trỏ, nó chỉ là: một con trỏ tới một đối tượng khác, dữ liệu duy nhất mà nó chứa là địa chỉ bộ nhớ mà nó trỏ đến. Vì vậy, ví dụ nếu tôi có một con trỏ int32 và gán giá trị trực tiếp cho nó, tôi sẽ không thay đổi giá trị của int, tôi đang chỉ đến một địa chỉ bộ nhớ mới (có rất nhiều thủ thuật gọn gàng bạn có thể làm với điều này ). Điều thú vị hơn nữa là có một con trỏ tới một con trỏ (đây là điều xảy ra khi bạn truyền biến Ref làm hàm Tham số trong C #, hàm có thể gán một đối tượng hoàn toàn khác cho Tham số và giá trị đó sẽ vẫn nằm trong phạm vi khi hàm hoạt động lối thoát hiểm

Đệ quy có một bước nhảy vọt về tinh thần khi học lần đầu tiên bởi vì bạn đang xác định một chức năng theo chính nó. Đó là một khái niệm hoang dã khi bạn lần đầu tiên bắt gặp nó, nhưng một khi bạn nắm bắt được ý tưởng, nó sẽ trở thành bản chất thứ hai.

Nhưng trở lại chủ đề trong tầm tay. Lập luận của Joel không phải là về con trỏ hoặc đệ quy trong chính họ, mà là thực tế là các sinh viên đang bị loại bỏ khỏi cách máy tính thực sự hoạt động. Đây là Khoa học về Khoa học Máy tính. Có một sự khác biệt rõ rệt giữa việc học lập trình và học cách các chương trình hoạt động. Tôi không nghĩ đó là vấn đề quá lớn "Tôi đã học theo cách này vì vậy mọi người nên học theo cách này" khi ông cho rằng nhiều chương trình CS đang trở thành trường thương mại được tôn vinh.


1

Tôi cho P. Brian +1, vì tôi cảm thấy như anh ta: đệ quy là một khái niệm cơ bản đến nỗi anh ta có những khó khăn nhỏ nhất với nó nên xem xét việc tìm kiếm một công việc tại mac donalds, nhưng sau đó, thậm chí còn có đệ quy:

make a burger:
   put a cold burger on the grill
   wait
   flip
   wait
   hand the fried burger over to the service personel
   unless its end of shift: make a burger

Chắc chắn, sự thiếu hiểu biết cũng có liên quan đến các trường học của chúng tôi. Ở đây người ta nên giới thiệu những con số tự nhiên như Peano, Dedekind và Frege đã làm, vì vậy chúng ta sẽ không gặp quá nhiều khó khăn sau này.


6
Đó là hồi tưởng đuôi, được cho là vòng lặp.
Michael K

6
Xin lỗi, với tôi, vòng lặp được cho là đệ quy đuôi :)
Ingo

3
@Ingo: :) Chức năng cuồng tín!
Michael K

1
@Michael - hehe, thực sự!, Nhưng tôi nghĩ người ta có thể làm cho trường hợp đệ quy là khái niệm cơ bản hơn.
Ingo

@Ingo: Bạn thực sự có thể (ví dụ của bạn chứng minh điều đó tốt). Tuy nhiên, vì một số lý do, con người gặp khó khăn với việc lập trình - chúng ta dường như muốn điều đó thêm goto topvì một số lý do IME.
Michael K

1

Tôi không đồng ý với Joel rằng vấn đề là một trong những suy nghĩ ở nhiều cấp độ trừu tượng, tôi nghĩ rằng nhiều vấn đề hơn và đệ quy là hai ví dụ điển hình về vấn đề đòi hỏi sự thay đổi trong mô hình tinh thần của mọi người về cách thức hoạt động của các chương trình.

Con trỏ là, tôi nghĩ, trường hợp đơn giản hơn để minh họa. Đối phó với các con trỏ đòi hỏi một mô hình tinh thần về thực thi chương trình, giải thích cho cách các chương trình thực sự hoạt động với địa chỉ bộ nhớ và dữ liệu. Kinh nghiệm của tôi là thường các lập trình viên thậm chí không nghĩ về điều này trước khi họ tìm hiểu về con trỏ. Ngay cả khi họ biết nó theo nghĩa trừu tượng, họ vẫn không áp dụng nó vào mô hình nhận thức của họ về cách thức một chương trình hoạt động. Khi con trỏ được giới thiệu, nó đòi hỏi một sự thay đổi cơ bản trong cách họ nghĩ về cách thức hoạt động của mã.

Đệ quy là vấn đề bởi vì có hai khối khái niệm để hiểu. Đầu tiên là ở cấp độ máy, và giống như con trỏ có thể khắc phục bằng cách phát triển sự hiểu biết tốt về cách các chương trình thực sự được lưu trữ và thực thi. Vấn đề khác với đệ quy là, tôi nghĩ rằng mọi người có xu hướng tự nhiên cố gắng giải mã một vấn đề đệ quy thành một vấn đề không đệ quy, làm vấy bẩn sự hiểu biết về chức năng đệ quy như một cử chỉ. Đây là một vấn đề với những người có nền tảng toán học không đủ hoặc một mô hình tinh thần không gắn kết lý thuyết toán học với sự phát triển của các chương trình.

Vấn đề là, tôi không nghĩ rằng con trỏ và đệ quy là hai lĩnh vực duy nhất có vấn đề đối với những người bị mắc kẹt trong một mô hình tinh thần không đủ. Song song dường như là một lĩnh vực khác mà một số người đơn giản gặp khó khăn và gặp khó khăn trong việc điều chỉnh mô hình tinh thần của họ để giải quyết, đó chỉ là những lần con trỏ và đệ quy dễ kiểm tra trong một cuộc phỏng vấn.


1
  DATA    |     CODE
          |
 pointer  |   recursion    SELF REFERENTIAL
----------+---------------------------------
 objects  |   macro        SELF MODIFYING
          |
          |

Khái niệm dữ liệu tự tham chiếu và mã làm cơ sở cho định nghĩa của con trỏ và đệ quy tương ứng. Thật không may, sự tiếp xúc rộng rãi với các ngôn ngữ lập trình cấp bách đã khiến sinh viên khoa học máy tính tin rằng họ phải hiểu việc thực hiện thông qua hành vi hoạt động của thời gian chạy của họ khi họ phải tin vào bí ẩn này về khía cạnh chức năng của ngôn ngữ. Tổng hợp tất cả các số lên đến một trăm dường như là một vấn đề đơn giản khi bắt đầu bằng một và thêm nó vào chuỗi tiếp theo và thực hiện ngược lại với sự trợ giúp của các chức năng tự tham chiếu vòng tròn có vẻ gian tà và thậm chí nguy hiểm đối với nhiều người không được sử dụng để đảm bảo an toàn cho chức năng thuần túy.

Khái niệm tự sửa đổi dữ liệu và mã làm cơ sở cho định nghĩa của các đối tượng (tức là dữ liệu thông minh) và macro tương ứng. Tôi đề cập đến những điều này vì chúng thậm chí còn khó hiểu hơn đặc biệt là khi sự hiểu biết vận hành về thời gian chạy được mong đợi từ sự kết hợp của cả bốn khái niệm - ví dụ như macro tạo ra một tập hợp các đối tượng thực hiện một trình phân tích cú pháp đệ quy với sự trợ giúp của một cây con trỏ . Thay vì theo dõi toàn bộ hoạt động của trạng thái từng bước của chương trình qua từng lớp trừu tượng cùng một lúc, các lập trình viên bắt buộc phải học cách tin rằng các biến của chúng chỉ được gán một lần trong các hàm thuần túy và các lệnh lặp lại của cùng một hàm thuần túy với các đối số tương tự luôn mang lại cùng một kết quả (nghĩa là độ trong suốt tham chiếu), ngay cả trong một ngôn ngữ cũng hỗ trợ các hàm không tinh khiết, như Java. Chạy lòng vòng trong vòng tròn sau thời gian chạy là một nỗ lực không có kết quả. Trừu tượng nên đơn giản hóa.


-1

Rất giống với câu trả lời của Anon.
Bên cạnh những khó khăn về nhận thức cho người mới, cả con trỏ và đệ quy đều rất mạnh mẽ và có thể được sử dụng theo những cách khó hiểu.

Nhược điểm của sức mạnh lớn, là chúng cung cấp cho bạn sức mạnh tuyệt vời để làm hỏng chương trình của bạn theo những cách tinh tế.
Lưu trữ một giá trị không có thật vào một biến bình thường là đủ tệ, nhưng lưu trữ một cái gì đó không có thật vào một con trỏ có thể khiến tất cả các loại thảm họa bị trì hoãn xảy ra.
Và tệ hơn, những hiệu ứng đó có thể thay đổi khi bạn cố gắng chẩn đoán / gỡ lỗi nguyên nhân của hành vi chương trình kỳ quái.

Tương tự với đệ quy. Nó có thể là một cách rất mạnh mẽ để tổ chức các công cụ phức tạp - bằng cách nhét sự khó khăn vào cấu trúc dữ liệu ẩn (ngăn xếp).
Nhưng, nếu một cái gì đó được thực hiện sai một cách tinh tế, có thể khó để tìm ra những gì đang xảy ra.

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.