Nếu bạn xây dựng các hoạt động tối thiểu của một máy tính chung từ đầu, "Lặp lại" trước tiên là một khối xây dựng và ít tốn tài nguyên hơn "đệ quy", ergo sẽ nhanh hơn.
Chúng tôi sẽ thiết lập một hệ thống phân cấp các khái niệm, bắt đầu từ đầu và xác định trước tiên các khái niệm cơ bản, cốt lõi, sau đó xây dựng các khái niệm cấp hai với các khái niệm đó, v.v.
Khái niệm đầu tiên: Các ô nhớ, lưu trữ, Nhà nước . Để làm một cái gì đó bạn cần nơi để lưu trữ giá trị kết quả cuối cùng và trung gian. Giả sử chúng ta có một mảng vô hạn các ô "số nguyên", được gọi là Bộ nhớ , M [0..Infinite].
Hướng dẫn: làm một cái gì đó - biến đổi một ô, thay đổi giá trị của nó. thay đổi trạng thái . Mỗi hướng dẫn thú vị thực hiện một sự chuyển đổi. Hướng dẫn cơ bản là:
a) Đặt và di chuyển các ô nhớ
- lưu trữ một giá trị vào bộ nhớ, ví dụ: lưu trữ 5 m [4]
- sao chép một giá trị sang vị trí khác: ví dụ: store m [4] m [8]
b) Logic và số học
- và, hoặc, xor, không
- thêm, phụ, mul, div. ví dụ: thêm m [7] m [8]
Một tác nhân thực thi : một lõi trong CPU hiện đại. Một "tác nhân" là một cái gì đó có thể thực hiện các hướng dẫn. Một tác nhân cũng có thể là một người theo thuật toán trên giấy.
Thứ tự các bước: một chuỗi các hướng dẫn : tức là: làm điều này trước, làm điều này sau, v.v ... Một chuỗi hướng dẫn bắt buộc. Ngay cả một biểu thức dòng là "một chuỗi các hướng dẫn bắt buộc". Nếu bạn có một biểu thức với một "thứ tự đánh giá" cụ thể thì bạn có các bước . Điều đó có nghĩa là thậm chí còn có một biểu thức tổng hợp duy nhất có ẩn bước Bước và cũng có một biến cục bộ ẩn (hãy gọi nó là Kết quả trực tiếp). ví dụ:
4 + 3 * 2 - 5
(- (+ (* 3 2) 4 ) 5)
(sub (add (mul 3 2) 4 ) 5)
Biểu thức trên bao hàm 3 bước với biến "kết quả" ẩn.
// pseudocode
1. result = (mul 3 2)
2. result = (add 4 result)
3. result = (sub result 5)
Vì vậy, ngay cả các biểu thức infix, vì bạn có một thứ tự đánh giá cụ thể, là một chuỗi các hướng dẫn bắt buộc . Biểu thức ngụ ý một chuỗi các hoạt động được thực hiện theo một thứ tự cụ thể và bởi vì có các bước , nên cũng có một biến trung gian "kết quả" ẩn.
Con trỏ lệnh : Nếu bạn có một chuỗi các bước, bạn cũng có một "con trỏ lệnh" ẩn. Con trỏ lệnh đánh dấu hướng dẫn tiếp theo và tiến lên sau khi đọc lệnh nhưng trước khi lệnh được thực thi.
Trong máy tính giả này, Con trỏ lệnh là một phần của Bộ nhớ . (Lưu ý: Thông thường, Con trỏ lệnh sẽ là một thanh ghi đặc biệt, trong một lõi CPU, nhưng ở đây chúng tôi sẽ đơn giản hóa các khái niệm và giả sử tất cả dữ liệu (bao gồm các thanh ghi) là một phần của Bộ nhớ Bộ nhớ
Nhảy - Khi bạn đã có số bước được đặt hàng và Con trỏ lệnh , bạn có thể áp dụng hướng dẫn " lưu trữ " để thay đổi giá trị của chính Con trỏ lệnh. Chúng tôi sẽ gọi việc sử dụng cụ thể này của hướng dẫn cửa hàng với một tên mới: Jump . Chúng tôi sử dụng một tên mới vì dễ nghĩ về nó như một khái niệm mới. Bằng cách thay đổi con trỏ lệnh, chúng tôi sẽ hướng dẫn nhân viên chuyển sang Bước x.
Lặp lại vô hạn : Bằng cách nhảy trở lại, bây giờ bạn có thể làm cho tác nhân "lặp lại" một số bước nhất định. Tại thời điểm này chúng ta có Lặp lại vô hạn.
1. mov 1000 m[30]
2. sub m[30] 1
3. jmp-to 2 // infinite loop
Có điều kiện - Thực hiện có điều kiện hướng dẫn. Với mệnh đề "có điều kiện", bạn có thể thực hiện một cách có điều kiện một trong một số hướng dẫn dựa trên trạng thái hiện tại (có thể được đặt bằng một lệnh trước đó).
Lặp lại đúng : Bây giờ với mệnh đề điều kiện , chúng ta có thể thoát khỏi vòng lặp vô hạn của lệnh nhảy trở lại . Bây giờ chúng ta có một vòng lặp có điều kiện và sau đó lặp đúng
1. mov 1000 m[30]
2. sub m[30] 1
3. (if not-zero) jump 2 // jump only if the previous
// sub instruction did not result in 0
// this loop will be repeated 1000 times
// here we have proper ***iteration***, a conditional loop.
Đặt tên : đặt tên cho một vị trí bộ nhớ cụ thể giữ dữ liệu hoặc giữ một bước . Đây chỉ là một "tiện lợi" để có. Chúng tôi không thêm bất kỳ hướng dẫn mới nào bằng cách có khả năng xác định tên của tên Cameron cho các vị trí bộ nhớ. Naming Naming không phải là một hướng dẫn cho các đại lý, nó chỉ là một tiện lợi cho chúng tôi. Đặt tên làm cho mã (tại thời điểm này) dễ đọc hơn và dễ thay đổi hơn.
#define counter m[30] // name a memory location
mov 1000 counter
loop: // name a instruction pointer location
sub counter 1
(if not-zero) jmp-to loop
Chương trình con một cấp : Giả sử có một loạt các bước bạn cần thực hiện thường xuyên. Bạn có thể lưu trữ các bước trong một vị trí được đặt tên trong bộ nhớ và sau đó nhảy đến vị trí đó khi bạn cần thực hiện chúng (gọi). Khi kết thúc chuỗi, bạn cần quay lại điểm gọi để tiếp tục thực hiện. Với cơ chế này, bạn đang tạo hướng dẫn mới (chương trình con) bằng cách soạn các hướng dẫn cốt lõi.
Thực hiện: (không yêu cầu khái niệm mới)
- Lưu con trỏ lệnh hiện tại vào vị trí bộ nhớ được xác định trước
- nhảy đến chương trình con
- ở cuối chương trình con, bạn truy xuất Con trỏ lệnh từ vị trí bộ nhớ được xác định trước, quay trở lại hướng dẫn sau của cuộc gọi ban đầu
Vấn đề với việc thực hiện một cấp : Bạn không thể gọi một chương trình con khác từ chương trình con. Nếu bạn làm như vậy, bạn sẽ ghi đè địa chỉ trả về (biến toàn cục), do đó bạn không thể lồng các cuộc gọi.
Để có một triển khai tốt hơn cho các chương trình con: Bạn cần một STACK
Ngăn xếp : Bạn xác định một không gian bộ nhớ để hoạt động như một "ngăn xếp", bạn có thể đẩy các giá trị trên các ngăn xếp trên ngăn xếp, và cũng có thể bật nhạc pop vào cuối cùng. Để triển khai một ngăn xếp, bạn sẽ cần một Con trỏ ngăn xếp (tương tự như Con trỏ lệnh) trỏ đến đầu con trỏ thực tế của ngăn xếp. Khi bạn đẩy đẩy một giá trị, con trỏ ngăn xếp giảm xuống và bạn lưu trữ giá trị. Khi bạn xuất hiện, bạn nhận được giá trị tại Con trỏ ngăn xếp thực tế và sau đó Con trỏ ngăn xếp được tăng lên.
Chương trình con Bây giờ chúng ta có một ngăn xếp, chúng ta có thể thực hiện các chương trình con phù hợp cho phép các cuộc gọi lồng nhau . Việc triển khai là tương tự, nhưng thay vì lưu trữ Con trỏ lệnh ở vị trí bộ nhớ được xác định trước, chúng tôi "đẩy" giá trị của IP trong ngăn xếp . Khi kết thúc chương trình con, chúng ta chỉ cần giá trị pop pop giá trị từ ngăn xếp, thực sự quay trở lại hướng dẫn sau cuộc gọi ban đầu . Việc thực hiện này, có một ngăn xếp trên mạng, cho phép gọi một chương trình con từ một chương trình con khác. Với việc triển khai này, chúng ta có thể tạo ra một số mức độ trừu tượng khi xác định các hướng dẫn mới là chương trình con, bằng cách sử dụng các hướng dẫn cốt lõi hoặc các chương trình con khác làm các khối xây dựng.
Đệ quy : Điều gì xảy ra khi một chương trình con gọi chính nó?. Điều này được gọi là "đệ quy".
Vấn đề: Ghi đè kết quả trung gian cục bộ mà chương trình con có thể được lưu trữ trong bộ nhớ. Vì bạn đang gọi / sử dụng lại các bước tương tự, nếu kết quả trung gian được lưu trữ trong các vị trí bộ nhớ được xác định trước (biến toàn cục), chúng sẽ bị ghi đè lên các cuộc gọi lồng nhau.
Giải pháp: Để cho phép đệ quy, chương trình con nên lưu trữ kết quả trung gian cục bộ trong ngăn xếp , do đó, trên mỗi cuộc gọi đệ quy (trực tiếp hoặc gián tiếp), kết quả trung gian được lưu trữ ở các vị trí bộ nhớ khác nhau.
...
Cuối cùng, lưu ý rằng bạn có nhiều cơ hội để sử dụng đệ quy. Bạn có Cấu trúc dữ liệu đệ quy ở mọi nơi, hiện bạn đang xem: các phần của DOM hỗ trợ những gì bạn đang đọc là RDS, biểu thức JSON là RDS, hệ thống tệp phân cấp trong máy tính của bạn là RDS, tức là: bạn có một thư mục gốc, chứa các tệp và thư mục, mọi thư mục chứa tệp và thư mục, mỗi thư mục chứa tệp và thư mục ...