Chương trình con lồng nhau JavaScript

Trong lập trình máy tính, hàm lồng nhau [hoặc thủ tục lồng nhau hoặc chương trình con] là một hàm được định nghĩa bên trong một hàm khác, hàm kèm theo. Do các quy tắc phạm vi đệ quy đơn giản, một hàm lồng nhau tự nó ẩn bên ngoài hàm kèm theo ngay lập tức của nó, nhưng có thể thấy [truy cập] tất cả các đối tượng cục bộ [dữ liệu, hàm, loại, v.v. ] của chức năng kèm theo ngay lập tức của nó cũng như của bất kỳ [các] chức năng nào bao hàm chức năng đó. Việc lồng ghép về mặt lý thuyết có thể thực hiện được với độ sâu không giới hạn, mặc dù chỉ có một số mức độ thường được sử dụng trong các chương trình thực tế

Các hàm lồng nhau được sử dụng trong nhiều cách tiếp cận lập trình có cấu trúc, bao gồm cả những cách tiếp cận ban đầu, chẳng hạn như ALGOL, Simula 67 và Pascal, cũng như trong nhiều ngôn ngữ động và ngôn ngữ hàm hiện đại. Tuy nhiên, theo truyền thống, chúng không được hỗ trợ trong họ ngôn ngữ C [đơn giản ban đầu]

Hiệu ứng[sửa]

Các hàm lồng nhau giả định phạm vi chức năng hoặc phạm vi khối. Phạm vi của một hàm lồng nhau nằm bên trong hàm kèm theo, tôi. e. bên trong một trong các khối cấu thành của chức năng đó, có nghĩa là nó không nhìn thấy bên ngoài khối đó và cả bên ngoài chức năng kèm theo. Một hàm lồng nhau có thể truy cập các hàm cục bộ khác, biến, hằng, kiểu, lớp, v.v. trong cùng một phạm vi hoặc trong bất kỳ phạm vi kèm theo nào mà không truyền tham số rõ ràng, giúp đơn giản hóa rất nhiều việc truyền dữ liệu vào và ra khỏi hàm lồng nhau. Điều này thường được phép cho cả đọc và viết

Các chức năng lồng nhau trong một số trường hợp [và ngôn ngữ] nhất định có thể dẫn đến việc tạo ra một bao đóng. Nếu hàm lồng nhau có thể thoát khỏi hàm bao quanh, ví dụ: nếu các hàm là đối tượng hạng nhất và một hàm lồng nhau được truyền cho một hàm khác hoặc được trả về từ hàm bao quanh, thì một bao đóng được tạo và các lệnh gọi đến hàm này có thể truy cập . Khung của hàm bao quanh ngay lập tức phải tiếp tục tồn tại cho đến khi lần đóng tham chiếu cuối cùng chết và do đó, các biến tự động không cục bộ được tham chiếu trong các lần đóng không thể được cấp phát ngăn xếp. Đây được gọi là vấn đề funarg và là lý do chính khiến các hàm lồng nhau không được triển khai trong một số ngôn ngữ đơn giản hơn vì nó làm phức tạp đáng kể việc tạo và phân tích mã, đặc biệt là khi các hàm được lồng vào nhiều cấp độ khác nhau, chia sẻ các phần khác nhau trong môi trường của chúng

Các ví dụ[sửa]

Một ví dụ sử dụng cú pháp Pascal [với ALGOL, Modula 2, Oberon, Ada, v.v. giống]

function E[x: real]: real;
    function F[y: real]: real;
    begin
        F := x + y
    end;
begin
    E := F[3] + F[4]
end;

Hàm F được lồng trong E. Lưu ý rằng tham số của E x cũng hiển thị trong F [vì F là một phần của E] trong khi cả x

fun e [x : real] =
  let
    fun f y = x+y
  in
    f 3 + f 4
  end;
4 đều vô hình bên ngoài EF tương ứng

Tương tự, trong Standard ML

fun e [x : real] =
  let
    fun f y = x+y
  in
    f 3 + f 4
  end;

Một cách để viết ví dụ tương tự trong cú pháp Haskell

e :: Float -> Float
e x = f 3 + f 4 where f y = x + y

Ví dụ tương tự trong cú pháp GNU C [C được mở rộng với các hàm lồng nhau]

float E[float x]
{
    float F[float y]
    {
        return x + y;
    }
    return F[3] + F[4];
}

Sắp xếp nhanh[sửa]

Một ví dụ thực tế hơn là việc triển khai quicksort này

void sort[int *items, int size] {
    void quickSort[int first, int last] {
        void swap[int p, int q] {
            int tmp = items[p];
            items[p] = items[q];
            items[q] = tmp;
        }
        
        int partition[] {
            int pivot = items[first], index = first;
            swap[index, last];
            for [int i = first; i < last; i++]
                if [items[i] < pivot]
                    swap[index++, i];
            swap[index, last];
            return index;
        }

        if [first < last] {
            int pivotIndex = partition[];
            quickSort[first, pivotIndex - 1];
            quickSort[pivotIndex + 1, last];
        }
    }
    quickSort[0, size - 1];
}

Một ví dụ khác là việc thực hiện sau đây của việc sử dụng

template
auto Sort[RandomAccessIterator Begin, RandomAccessIterator End]->void {
	auto Partition = [&][] {
		//Hoare partition scheme
		auto &Pivot = *Begin;
		auto ForwardCursor = Begin;
		auto BackwardCursor = End - 1;
		auto PartitionPositionFound = false;
		auto LocatePartitionPosition = [&][] {
			while [*ForwardCursor < Pivot]
				++ForwardCursor;
			while [Pivot < *BackwardCursor]
				--BackwardCursor;
			if [ForwardCursor >= BackwardCursor]
				PartitionPositionFound = true;
			else
				Swap[*ForwardCursor, *BackwardCursor];
		};
		//Trivial helper function
		auto MoveOnAndTryAgain = [&][] {
			++ForwardCursor;
			--BackwardCursor;
		};
		//Brief outline of the actual partition process
		while [true] {
			LocatePartitionPosition[];
			if [PartitionPositionFound]
				return BackwardCursor + 1;
			else
				MoveOnAndTryAgain[];
		}
	};
	//Brief outline of the quicksort algorithm
	if [Begin < End - 1] {
		auto PartitionPosition = Partition[];
		Sort[Begin, PartitionPosition];
		Sort[PartitionPosition, End];
	}
}

Mục đích[sửa]

Các định nghĩa hàm lồng nhau theo từ vựng là một hình thức che giấu thông tin và rất hữu ích để phân chia các nhiệm vụ thủ tục thành các nhiệm vụ con chỉ có ý nghĩa cục bộ. Điều này tránh làm lộn xộn các phần khác của chương trình với các hàm và biến không liên quan đến các phần đó

Chúng thường được sử dụng làm hàm trợ giúp hoặc hàm đệ quy bên trong một hàm khác [như trong ví dụ sắp xếp nhanh ở trên]. Điều này có lợi ích về cấu trúc trong việc tổ chức mã, tránh làm ô nhiễm phạm vi và cũng cho phép các chức năng chia sẻ trạng thái dễ dàng. Vì hàm lồng nhau có thể truy cập các biến cục bộ của hàm kèm theo, nên có thể chia sẻ trạng thái mà không cần truyền tham số cho hàm lồng nhau hoặc sử dụng biến toàn cục, giúp đơn giản hóa mã

Trong các ngôn ngữ có các hàm lồng nhau, các hàm thông thường cũng có thể chứa các hằng cục bộ và các kiểu [ngoài các biến, tham số và hàm cục bộ], được đóng gói và ẩn theo cùng một cách lồng nhau, ở bất kỳ mức độ sâu nào. Điều này có thể nâng cao hơn nữa khả năng cấu trúc mã

Sử dụng khác [ chỉnh sửa ]

Luồng điều khiển[sửa]

Các hàm lồng nhau cũng có thể được sử dụng cho luồng điều khiển phi cấu trúc, bằng cách sử dụng câu lệnh return cho luồng điều khiển phi cấu trúc chung. Điều này có thể được sử dụng để kiểm soát chi tiết hơn khả năng có thể với các tính năng tích hợp sẵn khác của ngôn ngữ – ví dụ: nó có thể cho phép kết thúc sớm vòng lặp for nếu không có sẵn

fun e [x : real] =
  let
    fun f y = x+y
  in
    f 3 + f 4
  end;
7 hoặc kết thúc sớm vòng lặp for lồng nhau nếu có

Hàm bậc cao[sửa]

Vì trong hầu hết các ngôn ngữ, các hàm là các kiểu trả về hợp lệ, có thể tạo một hàm lồng nhau truy cập một tập hợp các tham số từ hàm bên ngoài và có hàm đó là giá trị trả về của hàm bên ngoài. Do đó, có thể trả về một hàm được đặt để hoàn thành một tác vụ nhất định với ít hoặc không có thêm tham số nào được cung cấp cho nó, điều này có thể làm tăng hiệu suất khá đáng kể

Các lựa chọn thay thế[sửa]

Giải pháp thay thế chính cho các hàm lồng nhau trong các ngôn ngữ thiếu hỗ trợ cho chúng là đặt tất cả các hàm và biến có liên quan trong một mô-đun [tệp] riêng biệt và chỉ hiển thị công khai hàm bao bọc cấp cao nhất. Trong C, điều này thường sẽ được thực hiện bằng cách sử dụng các hàm tĩnh để đóng gói và các biến tĩnh để liên lạc. Điều này đạt được sự đóng gói và chia sẻ trạng thái, mặc dù không phải là tổ chức hợp lý được cung cấp bởi việc lồng các hàm từ vựng và phải trả giá bằng việc có một tệp riêng biệt. Nó cũng không thể ở nhiều hơn một cấp độ

Một cách khác là chia sẻ trạng thái giữa các hàm thông qua các tham số hàm, thường xuyên nhất là chuyển các tham chiếu dưới dạng đối số để tránh chi phí sao chép. Trong C, điều này thường được thực hiện bởi một con trỏ tới cấu trúc chứa ngữ cảnh. Điều này làm tăng đáng kể độ phức tạp của các lệnh gọi hàm

Trong PHP và các ngôn ngữ khác, chức năng ẩn danh là giải pháp thay thế duy nhất. hàm lồng nhau được khai báo không phải như hàm thông thường, mà theo tham chiếu, như một biến cục bộ. Để sử dụng các biến cục bộ trong hàm ẩn danh, hãy sử dụng bao đóng

Ngôn ngữ[sửa]

Các ngôn ngữ nổi tiếng hỗ trợ các chức năng lồng nhau từ vựng bao gồm

Ngôn ngữ chức năng [ chỉnh sửa ]

Trong hầu hết các ngôn ngữ lập trình hàm, chẳng hạn như Scheme, các hàm lồng nhau là một cách phổ biến để triển khai các thuật toán có vòng lặp trong đó. Một hàm bên trong đệ quy [đuôi] đơn giản được tạo, hoạt động như vòng lặp chính của thuật toán, trong khi hàm bên ngoài thực hiện các hành động khởi động chỉ cần thực hiện một lần. Trong các trường hợp phức tạp hơn, một số hàm đệ quy lẫn nhau có thể được tạo dưới dạng các hàm bên trong

Một số ngôn ngữ không có hỗ trợ trực tiếp[sửa | sửa mã nguồn]

Một số ngôn ngữ không có hỗ trợ cú pháp và ngữ nghĩa đơn giản để triển khai các hàm lồng nhau. Tuy nhiên, đối với một số người trong số họ, ý tưởng về các hàm lồng nhau có thể được mô phỏng với một số mức độ khó khăn thông qua việc sử dụng các cấu trúc ngôn ngữ khác. Các ngôn ngữ sau có thể xấp xỉ các hàm lồng nhau thông qua các chiến lược tương ứng

  • C++
    • trước C++11. cho phép định nghĩa các lớp bên trong các lớp, cung cấp khả năng sử dụng các phương thức của lớp theo cách tương tự như các hàm lồng nhau ở một mức [xem phần ]
    • kể từ C++11. bằng cách sử dụng biểu thức lambda như ví dụ sắp xếp nhanh ở trên
  • Eiffel rõ ràng không cho phép lồng các thói quen. Điều này là để giữ cho ngôn ngữ đơn giản và cũng cho phép quy ước sử dụng một biến đặc biệt, Kết quả, để biểu thị kết quả của một hàm [trả về giá trị]
  • Visual Basic, bằng cách sử dụng các phương thức ẩn danh hoặc biểu thức lambda
  • Java, bằng cách sử dụng các biểu thức lambda [xem ] [kể từ Java 8] hoặc thông qua một giải pháp thay thế bao gồm một lớp ẩn danh chứa một phương thức duy nhất. Một lớp được đặt tên được khai báo cục bộ cho một phương thức cũng có thể được sử dụng

Thực hiện[sửa]

Việc triển khai các hàm lồng nhau có thể liên quan nhiều hơn so với vẻ ngoài của nó, vì tham chiếu đến hàm lồng nhau tham chiếu đến các biến không cục bộ sẽ tạo ra một bao đóng. Vì lý do này, các hàm lồng nhau không được hỗ trợ trong một số ngôn ngữ như C, C++ hoặc Java vì điều này làm cho trình biên dịch khó triển khai hơn. Tuy nhiên, một số trình biên dịch hỗ trợ chúng, như một phần mở rộng dành riêng cho trình biên dịch. Một ví dụ nổi tiếng về điều này là việc triển khai GNU C của C chia sẻ mã với trình biên dịch cho các ngôn ngữ như Pascal, Ada và Modula

Truy cập các đối tượng không cục bộ[sửa | sửa mã nguồn]

Có một số cách để thực hiện các thủ tục lồng nhau trong ngôn ngữ có phạm vi từ vựng, nhưng cách cổ điển như sau

Bất kỳ đối tượng không cục bộ nào, X, được tiếp cận thông qua các liên kết truy cập trong các khung kích hoạt trên ngăn xếp máy. Người gọi, C, hỗ trợ thủ tục được gọi, P, bằng cách đẩy một liên kết trực tiếp tới lần kích hoạt mới nhất của đóng gói từ vựng tức thì của P, [P], trước chính cuộc gọi đó. Sau đó, P có thể nhanh chóng tìm ra cách kích hoạt phù hợp cho một X nhất định bằng cách lần theo một số cố định [P. độ sâu – X. độ sâu] của các liên kết [thường là một số lượng nhỏ]. Người gọi tạo liên kết trực tiếp này bằng [chính nó] sau C. độ sâu – P. độ sâu + 1 liên kết cũ hơn, dẫn đến kích hoạt mới nhất của [P], sau đó tạm thời kết nối các liên kết này với liên kết trực tiếp đến kích hoạt đó; . Lưu ý rằng P hiển thị cho và do đó có thể được gọi bởi C nếu [P] = C / [C] / [[C]] / v.v.

Phương pháp ban đầu này nhanh hơn vẻ ngoài của nó, tuy nhiên nó thường được tối ưu hóa trong các trình biên dịch hiện đại thực tế [sử dụng hoặc các kỹ thuật tương tự]

Một cách khác để triển khai các hàm lồng nhau được một số trình biên dịch sử dụng là chuyển đổi ["nâng"] các hàm lồng nhau thành các hàm không lồng nhau [trong đó các tham số bổ sung, ẩn, thay thế các liên kết truy cập] bằng cách sử dụng quy trình được gọi là nâng lambda trong giai đoạn trung gian

Chức năng như giá trị[sửa]

Để các hàm cục bộ có các thuộc tính không cục bộ có phạm vi từ vựng được chuyển thành kết quả, mã thời gian chạy ngôn ngữ cũng phải hoàn toàn chuyển môi trường [dữ liệu] mà hàm nhìn thấy bên trong hàm đóng gói của nó, để nó cũng có thể truy cập được khi kích hoạt hiện tại của phần bao quanh . Điều này có nghĩa là môi trường phải được lưu trữ trong một vùng bộ nhớ khác với [các phần được lấy lại sau đó của] ngăn xếp thực thi dựa trên trình tự thời gian, do đó, ngụ ý một số loại cấp phát bộ nhớ động tự do. Do đó, nhiều ngôn ngữ dựa trên Algol cũ hơn [hoặc phương ngữ của chúng] không cho phép các hàm cục bộ truy cập các giá trị không phải là ngôn ngữ cục bộ được truyền dưới dạng giá trị trả về hoặc chúng hoàn toàn không cho phép các hàm làm giá trị trả về, mặc dù vẫn có thể truyền các hàm đó dưới dạng đối số

Ngăn xếp không thực thi[sửa]

Ít nhất một lần triển khai các hàm lồng nhau gây ra mất ngăn xếp Không thực thi [ngăn xếp NX]. Việc triển khai hàm lồng nhau của GCC gọi các hàm lồng nhau thông qua lệnh nhảy được đặt trong ngăn xếp máy khi chạy. Điều này yêu cầu ngăn xếp phải được thực thi

Không có ngăn xếp thực thi và các chức năng lồng nhau loại trừ lẫn nhau trong GCC. Nếu một hàm lồng nhau được sử dụng trong quá trình phát triển chương trình, thì Ngăn xếp NX sẽ bị mất một cách âm thầm. GCC đưa ra cảnh báo

fun e [x : real] =
  let
    fun f y = x+y
  in
    f 3 + f 4
  end;
9 để cảnh báo về tình trạng

Phần mềm được thiết kế bằng Vòng đời phát triển an toàn thường không cho phép sử dụng các hàm lồng nhau trong trình biên dịch [GCC] cụ thể này do mất Ngăn xếp NX

Tại sao các chương trình con lồng nhau không được phép?

Điều này là do các định nghĩa hàm lồng nhau không có quyền truy cập vào các biến cục bộ của khối xung quanh -- chỉ với các biến toàn cục của khối .

Làm tổ trong JavaScript là gì?

Lồng nhau là khi bạn viết nội dung nào đó bên trong nội dung khác . Bạn có thể có một chức năng bên trong một chức năng khác. function x[] { function y[] { // cái gì đó; . if [ánh sáng ban ngày] { if [trước 12 giờ] { // Trời sáng;

Làm cách nào để tạo một hàm lồng nhau trong js?

Viết một hàm bên trong một hàm khác. Thực hiện cuộc gọi đến hàm bên trong trong câu lệnh trả về của hàm bên ngoài. Gọi nó là fun[a][b] trong đó a là tham số của hàm bên ngoài và b là hàm bên trong. Cuối cùng, trả về đầu ra kết hợp từ hàm lồng nhau

Ngôn ngữ nào hỗ trợ các chương trình con lồng nhau?

Nhiều ngôn ngữ lập trình hỗ trợ các hàm lồng nhau bao gồm ALGOL, Pascal, Modula 2, Oberon, Ada, Python, Swift, D, Javascript . Các hàm lồng nhau rất hữu ích với vai trò là hàm trợ giúp, gọi lại, làm trình tạo, lập trình bậc cao hơn, v.v.

Chủ Đề