중첩 함수

Nested function

컴퓨터 프로그래밍에서 중첩된 함수(또는 중첩된 프로시저 또는 서브루틴)는 다른 함수인 둘러싸인 함수 내에 정의되는 함수입니다.단순한 재귀적 범위 규칙 때문에 중첩된 함수는 그 자체를 바로 둘러싸는 함수 밖에서는 볼 수 없지만, 바로 둘러싸인 함수의 모든 로컬 객체(데이터, 함수, 유형 등)를 볼 수 있습니다.이론적으로 네스팅은 무제한 깊이까지 가능하지만, 일반적으로 실용적인 프로그램에서는 몇 가지 수준만 사용됩니다.

중첩된 함수는 ALGOL, Simula 67 Pascal과 같은 초기 프로그래밍 및 많은 현대 동적 언어 및 함수 언어에 사용됩니다.단, 기존에는 (원래 단순한) C패밀리의 언어에서는 지원되지 않습니다.

영향들

중첩된 함수는 함수 범위 또는 블록 범위를 가정합니다.내포된 함수의 범위는 포위 함수 내부, 즉 해당 함수의 구성 블록 중 하나 안에 있습니다. 즉, 해당 블록 밖에서는 보이지 않으며 포위 함수 밖에서도 보이지 않습니다.중첩된 함수는 명시적 매개 변수 전달 없이 동일한 범위 또는 둘러싸인 범위에 있는 다른 로컬 함수, 변수, 상수, 유형, 클래스 등에 액세스할 수 있으므로 중첩된 함수에 대한 데이터 전달이 크게 간소화됩니다.이것은 일반적으로 읽기와 쓰기 모두에 대해 허용됩니다.

특정 상황(및 언어)에서 중첩된 함수로 인해 폐쇄가 생성될 수 있습니다.예를 들어 함수가 퍼스트 클래스 객체이고 네스트된 함수가 다른 함수에 전달되거나 인클로징 함수에서 반환되는 경우, 네스트된 함수가 인클로징 함수를 벗어날 수 있는 경우 클로저가 생성되고 이 함수에 대한 호출이 원래 함수의 환경에 액세스할 수 있습니다.바로 주변 함수의 프레임은 마지막 참조 폐쇄가 종료될 때까지 계속 활성 상태여야 하며, 따라서 폐쇄에서 참조되는 비로컬 자동 변수를 스택으로 할당할 수 없습니다.이는 funarg 문제로 알려져 있으며, 특히 함수가 다양한 수준으로 중첩되어 환경의 다른 부분을 공유하는 경우 코드 생성 및 분석이 상당히 복잡하기 때문에 일부 단순한 언어로 중첩된 함수가 구현되지 않은 주요 이유입니다.

Pascal 구문을 사용하는 예(ALGOL, Modula 2, Oberon, Ada 등 유사)

기능. E(x: 진짜): 진짜;     기능. F(y: 진짜): 진짜;     시작한다.         F := x + y     끝.; 시작한다.     E := F(3) + F(4) 끝.; 

함수F에 내포되어 있다.E주의해 주세요.E의 파라미터x에서도 볼 수 있습니다.F(과 같이)F의 일부입니다.E)과 동시에x그리고.y밖에서는 보이지 않는다E그리고.F각각 다음과 같다.

마찬가지로 표준 ML에서는:

재밌어요 e (x : 진짜) =   허락하다     재밌어요 f y = x+y        f 3 + f 4   끝.; 

같은 예를 Haskell 구문에 쓰는 방법 중 하나는 다음과 같습니다.

e :: 흘러가다 -> 흘러가다 e x = f 3 + f 4 어디에 f y = x + y 

GNU C[1] 구문에서도 같은 예(네스트된 함수를 사용하여 확장되는 C):

흘러가다 E(흘러가다 x) {     흘러가다 F(흘러가다 y)     {         돌아가다 x + y;     }     돌아가다 F(3) + F(4); } 

퀵소트

보다 현실적인 예는 다음과 같은 QuickSort [2]구현입니다.

무효 종류(인트 *항목들, 인트 크기) {     무효 퀵소트(인트 첫번째, 인트 지난) {         무효 바꾸다(인트 p, 인트 q) {             인트 tmp = 항목들[p];             항목들[p] = 항목들[q];             항목들[q] = tmp;         }                  인트 칸막이() {             인트 피벗 = 항목들[첫번째], 색인 = 첫번째;             바꾸다(색인, 지난);             위해서 (인트 i = 첫번째; i < > 지난; i++)                 한다면 (항목들[i] < > 피벗)                     바꾸다(색인++, i);             바꾸다(색인, 지난);             돌아가다 색인;         }          한다면 (첫번째 < > 지난) {             인트 피벗 인덱스 = 칸막이();             퀵소트(첫번째, 피벗 인덱스 - 1);             퀵소트(피벗 인덱스 + 1, 지난);         }     }     퀵소트(0, 크기 - 1); } 

또 다른 예로는 C++11 lambda 식 구문을 사용Hoare 파티션베이스의 퀵소트 구현이 있습니다.

템플릿< >타이프네임 랜덤 액세스반복기> 자동 종류(랜덤 액세스반복기 시작한다., 랜덤 액세스반복기 끝.)->무효 {  자동 파티션 = [&]() {   //호어 파티션 구성표   자동 &피벗 = *시작한다.;   자동 Forward Cursor(전송 커서) = 시작한다.;   자동 Backward Cursor(백커서) = 끝. - 1;   자동 Partition Position Found(파티션 위치 발견) = 거짓의;   자동 파티션 위치 지정 = [&]() {    하는 동안에 (*Forward Cursor(전송 커서) < > 피벗)     ++Forward Cursor(전송 커서);    하는 동안에 (피벗 < > *Backward Cursor(백커서))     --Backward Cursor(백커서);    한다면 (Forward Cursor(전송 커서) >= Backward Cursor(백커서))     Partition Position Found(파티션 위치 발견) = 진실의;    또 다른     바꾸다(*Forward Cursor(전송 커서), *Backward Cursor(백커서));   };   //자질 도우미 기능   자동 MoveOnAndTryAgain(이동 후 재시도) = [&]() {    ++Forward Cursor(전송 커서);    --Backward Cursor(백커서);   };   //실제 파티션 프로세스의 개요   하는 동안에 (진실의) {    파티션 위치 지정();    한다면 (Partition Position Found(파티션 위치 발견))     돌아가다 Backward Cursor(백커서) + 1;    또 다른     MoveOnAndTryAgain(이동 후 재시도)();   }  };  //QuickSort 알고리즘 개요  한다면 (시작한다. < > 끝. - 1) {   자동 파티션 위치 = 파티션();   종류(시작한다., 파티션 위치);   종류(파티션 위치, 끝.);  } } 

목적

어휘적으로 중첩된 함수 정의는 정보 숨김의 한 형태이며 절차 작업을 로컬에서만 의미가 있는 하위 작업으로 분할하는 데 유용합니다.이것에 의해, 프로그램의 다른 부분과 관련 없는 함수나 변수가 복잡하게 되는 것을 피할 수 있습니다.

일반적으로 도우미 함수 또는 다른 함수 내의 재귀 함수로 사용됩니다(위의 QuickSort 예시와 같음).이를 통해 코드를 정리할 수 있다는 구조적 이점이 있으며 스코프의 오염을 방지하고 함수가 [3]상태를 쉽게 공유할 수 있습니다.중첩된 함수는 내포된 함수의 로컬 변수에 액세스할 수 있으므로 매개 변수를 중첩된 함수에 전달하거나 전역 변수인 단순화된 코드를 사용하지 않고도 상태 공유가 가능하다.

중첩된 함수가 있는 언어에서는 일반적으로 함수에 로컬 상수 유형(로컬 변수, 파라미터 및 함수 외에)을 포함할 수 있으며, 동일한 중첩된 방식으로 캡슐화 및 숨겨진 깊이 수준을 지정할 수 있습니다.이것에 의해, 코드 구조화의 가능성이 한층 더 향상될 가능성이 있습니다.

기타 용도

흐름 제어

네스트된 함수는 일반 비구조화 제어 흐름의 return 문을 사용함으로써 비구조화 제어 흐름에도 사용할 수 있습니다.이것은 언어의 다른 내장 기능보다 세밀한 제어에 사용할 수 있습니다.예를 들어, for 루프의 조기 종료를 가능하게 하는 경우가 있습니다.break사용할 수 없습니다.또는 멀티레벨의 경우 루프에 대해 네스트된 것을 조기 종료break또는 예외를 사용할 수 없습니다.

고차 함수

대부분의 언어에서 함수는 유효한 반환 유형이기 때문에 외부 함수에서 파라미터 집합에 액세스하는 중첩 함수를 생성하여 해당 함수가 외부 함수의 반환 값이 되도록 할 수 있습니다.따라서 특정 작업을 수행하도록 설정된 함수를 더 이상 매개 변수를 거의 또는 전혀 지정하지 않고 반환할 수 있으므로 성능이 상당히 [4]향상될 수 있습니다.

대체 수단

지원이 부족한 언어에서 중첩된 함수의 주요 대안은 모든 관련 함수 및 변수를 별도의 모듈(파일)에 배치하고 최상위 래퍼 함수만 공개하는 것입니다.C에서는 일반적으로 캡슐화에는 정적 함수를 사용하고 [5]통신에는 정적 변수를 사용하여 이를 수행합니다.이는 함수의 어휘적 중첩에 의해 주어지는 논리적인 구성은 아니지만 캡슐화와 상태 공유를 실현하고 별도의 파일을 갖는 대가를 치르게 됩니다.또한 단일 수준 이상에서는 가능하지 않습니다.

또 다른 대안은 함수 매개변수를 통해 함수 간에 상태를 공유하는 것이며, 대부분의 경우 복사 비용을 피하기 위해 참조를 인수로 전달합니다.C에서 이것은 일반적으로 [5]컨텍스트를 포함하는 구조에 대한 포인터에 의해 구현됩니다.이것에 의해, [3]함수 호출의 복잡성이 큰폭으로 증가합니다.

PHP 및 기타 언어에서는 익명 함수가 유일한 대안입니다. 즉, 중첩된 함수는 일반 함수가 아니라 참조에 의해 로컬 변수로 선언됩니다.익명 함수에 로컬 변수를 사용하려면 닫기를 사용합니다.

언어들

어휘적으로 중첩된 함수를 지원하는 잘 알려진 언어는 다음과 같습니다.

기능 언어

Scheme와 같은 대부분의 기능적 프로그래밍 언어에서 중첩된 함수는 루프가 있는 알고리즘을 구현하는 일반적인 방법입니다.알고리즘의 메인 루프로서 동작하는 단순한(테일) 재귀 내부 함수가 작성되고, 외부 함수는 1회만 하면 되는 기동 액션을 실행한다.보다 복잡한 경우에는 내부 함수로 상호 재귀적 함수가 다수 생성될 수 있습니다.

일부 언어(직접 지원되지 않음)

일부 언어에는 중첩된 함수를 구현하기 위한 간단한 구문 및 의미 지원이 없습니다.그럼에도 불구하고, 그들 중 일부는 다른 언어 구조를 사용하여 중첩된 함수의 개념을 어느 정도 어렵게 시뮬레이션할 수 있다.다음 언어는 각각의 전략을 통해 중첩된 함수에 근접할 수 있습니다.

  • C++
    • C++11 이전: 클래스 내에서 클래스를 정의할 수 있으므로 클래스 메서드를 한 레벨에서 중첩된 함수와 유사한 방식으로 사용할 수 있습니다(C++의 함수 개체 참조).
    • 위의 빠른 정렬 예제로 [10]람다 식을 사용하여 C++11부터입니다.
  • 에펠은 일상의 보금자리를 명시적으로 허용하지 않는다.이는 언어를 단순하게 유지하기 위한 것으로, (값 반환) 함수의 결과를 나타내기 위한 특수 변수인 결과(Result)를 사용하는 규칙을 허용합니다.
  • Visual Basic, 익명 메서드 또는 람다 식을 사용합니다.
  • Java, lamda[11] 표현식 사용(Java 8 이후) 또는 단일 메서드를 포함하는 익명 클래스로 구성된 해결 방법을 사용합니다.메서드에 대해 로컬로 선언된 이름 있는 클래스를 사용할 수도 있습니다.

실행

로컬 변수가 아닌 변수를 참조하는 중첩 함수에 대한 참조가 닫힘을 생성하므로 중첩 함수의 구현은 보기보다 더 복잡할 수 있습니다.따라서 C, C++, Java 등의 일부 언어에서는 네스트된 함수가 지원되지 않습니다.이러한 함수는 컴파일러의 [5][12]실장을 더욱 어렵게 하기 때문입니다.다만, 컴파일러 고유의 확장으로서 이들을 서포트하고 있는 컴파일러도 있습니다.잘 알려진 예로는 Pascal, Ada, Modula 등의 언어용 컴파일러와 코드를 공유하는 C의 GNU C 구현이 있습니다.

로컬이 아닌 오브젝트 액세스

사전 스코프 언어로 중첩된 프로시저를 구현하는 방법은 여러 가지가 있지만 일반적인 방법은 다음과 같습니다.

로컬이 아닌 오브젝트 X는 머신스택의 액티베이션프레임 내의 액세스링크를 통해 도달합니다.발신자 C는, 콜 그 전에, P 의 어휘 캡슐화(P)의 최신의 액티브화에의 직접 링크를 푸시 하는 것으로, 착신 프로시저 P 를 서포트합니다.그 후, P는 고정수의 링크(통상 적은 수)에 따라 특정의 X 에 적절한 액티베이션(P.depth – X.depth)을 신속히 검출할 수 있습니다.
발신자는 C.depth – P.depth +1개의 오래된 링크에 이어 이 직접 링크를 작성합니다.이 링크를 통해 (P.depth + 1개의 오래된 링크를 통해) 최신 액티베이션으로 이어집니다.이 링크는 나중에 P와 함께 사라집니다.그러면 아래에 있는 오래된 링크가 다시 사용될 수 있습니다.
(P) = C / (C) / (C) / (C) / 기타일 경우 P는 C에 대해 볼 수 있으며 따라서 C로 호출될 수 있다.

이 원래의 방법은 보기보다 빠르지만 실제적인 최신 컴파일러(디스플레이 또는 유사한 기술을 사용)에 최적화되는 경우가 많습니다.

일부 컴파일러가 사용하는 네스트 함수를 구현하는 또 다른 방법은 컴파일러의 중간 단계에서 람다 리프팅이라고 하는 프로세스를 사용하여 네스트된 함수를 네스트되지 않은 함수(추가, 숨겨진 파라미터가 액세스 링크를 대체하는 경우)로 변환하는 것입니다.

가치관으로서의 기능

어휘 범위 논로컬을 가진 로컬 함수가 결과로 전달되기 위해서는 언어 런타임 코드도 함수가 캡슐화 함수 내에 보는 환경(데이터)을 암묵적으로 전달해야 하며, 따라서 인클로징 함수의 현재 활성화가 존재하지 않는 [13]경우에도 도달할 수 있어야 합니다.즉, 환경은 시간순 기반 실행 스택(이후 재생되는 부분)이 아닌 다른 메모리 영역에 저장되어야 하며, 이는 일종의 자유로운 동적 메모리 할당을 의미합니다.따라서 많은 오래된 Algol 기반 언어(또는 그 방언)는 논로컬에 액세스하는 로컬 함수가 반환값으로 전달되는 것을 허용하지 않으며, 인수와 같은 함수의 전달은 여전히 가능하지만 함수는 반환값으로 전달되는 것을 전혀 허용하지 않습니다.

실행하지 않는 스택

중첩된 함수를 하나 이상 구현하면 실행 중지 스택(NX 스택)이 손실됩니다.GCC의 중첩된 함수 구현은 런타임에 머신 스택에 삽입된 점프 명령을 통해 중첩된 함수를 호출합니다.이를 위해서는 스택이 실행 가능해야 합니다.

실행 스택과 중첩된 함수는 GCC에서 서로 배타적입니다.프로그램 개발에 네스트된 함수를 사용하면 NX Stack은 자동으로 손실됩니다.GCC는-Wtrampoline경고(warnowledging to alert of the status.

시큐어 개발 라이프 사이클을 사용해 설계된 소프트웨어에서는,[14] NX 스택의 손실로 인해, 이 특정의 컴파일러(GCC)에서 네스트 된 함수를 사용할 수 없는 경우가 많습니다.

「 」를 참조해 주세요.

메모들

레퍼런스

  1. ^ Rothwell, Trevis J. (2011). The GNU C Reference Manual. Free Software Foundation, Inc. p. 63.
  2. ^ 참조: 네스팅 기능 - 왜?, baavgai, 2012년 1월 14일
  3. ^ a b 밝은 2004년
  4. ^ 고차 함수 및 람다 - Kotlin 프로그래밍 언어
  5. ^ a b c 질문 20.24: C에 중첩된 함수가 없는 이유는 무엇입니까? comp.lang.c FAQ
  6. ^ "A tour of the Dart language".
  7. ^ "Functions Kotlin".
  8. ^ "Nested Methods".
  9. ^ "Nested Functions – Using the GNU Compiler Collection (GCC)". GNU Project. Retrieved 2007-01-06.
  10. ^ "Nested function - Rosetta Code".
  11. ^ "Nested function - Rosetta Code".
  12. ^ Dave Vandervies, 2009년 8월 28일 17:45에 "왜 중첩된 함수는 C 표준에서 지원되지 않는가?"라는 질문에 대한 답변입니다.
  13. ^ 이러한 기능 코드와 그 환경의 조합을 폐쇄라고 부르기도 합니다.
  14. ^ Walton, Jeffrey. "C-Based Toolchain Hardening". The Open Web Application Security Project (OWASP). Retrieved 28 February 2017.

외부 링크