함수(컴퓨터 프로그래밍)

Function (computer programming)

컴퓨터 프로그래밍에서 기능, 하위 프로그램, 절차, 방법, 루틴 또는 서브루틴이라고 하는 기술은 잘 정의된 동작을 가지며 컴퓨터 프로그램 내에서 호출하여 동작을 나타낼 수 있는 일련의 명령어입니다.

이 기술은 일반적으로 호출 가능한 장치라고 불립니다.[1] 호출 가능한 장치는 강력한 프로그래밍 도구를 제공합니다.[2] 주요 목적은 크고 복잡한 문제를 상대적으로 인지 부하가 낮은 청크로 분해하고 (익명이 아닌 한) 의미 있는 이름을 할당하는 것입니다.

현명한 응용 프로그램은 소프트웨어 개발 및 유지 관리 비용을 줄이는 동시에 품질과 신뢰성을 높일 수 있습니다.[3]

이 기술은 프로그래밍 환경에서 여러 수준의 추상화로 존재합니다. 예를 들어, 프로그래머는 유사한 의미론을 구현하는 머신 코드에 컴파일된 함수를 소스 코드에 쓸 수 있습니다. 소스 코드에는 호출 가능한 유닛이 있고 머신 코드에는 관련된 유닛이 있지만, 이들은 다른 종류의 호출 가능한 유닛이며, 다른 의미와 기능을 가지고 있습니다.

역사

호출 가능한 단위 기술은 처음에 존 모츨리와 캐슬린 안토넬리ENIAC에서 연구하는 동안 구상했으며 [4]1947년 1월 "EDVAC형 기계를 위한 문제의 준비"에 관한 하버드 심포지엄에서 기록했습니다.[5] 모리스 윌크스(Maurice Wilkes), 데이비드 휠러(David Wheeler), 스탠리 길(Stanley Gill)은 일반적으로 이 개념을 공식적으로 발명한 것으로 알려져 있으며,[6][7] 그들은 이 개념을 개방형 서브루틴 또는 매크로와 대조하여 폐쇄형 서브루틴이라고 불렀습니다.[8] 그러나 Alan Turing은 1945년 NPLACE에 대한 설계 제안에 대한 논문에서 서브루틴에 대해 논의했으며, 리턴 주소 스택의 개념을 발명하는 데까지 나아갔습니다.[9]

서브루틴의 아이디어는 컴퓨팅 머신이 이미 존재한 지 얼마 되지 않아 해결되었습니다. 산술 및 조건부 점프 명령어는 사전에 계획된 것으로 비교적 변화가 적었지만, 시술 호출에 사용되는 특수 명령어는 수년간 크게 변화했습니다. 맨체스터 베이비RCA 1802와 같은 초기 컴퓨터와 마이크로프로세서에는 단일 서브루틴 호출 명령어가 없었습니다. 서브루틴은 구현될 수 있지만 프로그래머들이 각 호출 사이트에서 일련의 명령어인 호출 시퀀스를 사용하도록 요구했습니다.

서브루틴은 1945년 콘라드 주세Z4에 구현되었습니다.

1945년, Alan M. 튜링은 "bury"와 "unbury"라는 용어를 서브루틴에서 전화를 걸고 돌아오는 수단으로 사용했습니다.[10][11]

1947년 1월 존 모츨리는 하버드 대학교와 미국 해군 병무청의 공동 후원으로 '대규모 디지털 계산 기계 심포지엄'에서 일반 노트를 발표했습니다. 여기서 그는 다음을 제안하는 직렬 및 병렬 연산에 대해 설명합니다.

...기계의 구조는 한 조각도 복잡할 필요가 없습니다. 이 절차에 필수적인 모든 논리적 특성을 사용할 수 있기 때문에, 서브루틴을 기계에 알려진 장소에 메모리에 배치하기 위한 코딩 명령어를 쉽게 사용하도록 진화시킬 수 있습니다.

즉, 특정 문제에 필요한 서브루틴 목록을 통해 서브루틴 A를 나눗셈으로, 서브루틴 B를 복소수 곱셈으로, 서브루틴 C를 수열의 표준오차 평가로 지정할 수 있습니다. 그런 다음 이러한 모든 하위 루틴이 기계에 저장되며 코딩에 표시된 대로 번호로 간단히 참조하기만 하면 됩니다.[5]

케이 맥널티는 존 모클리(John Mauchly)와 함께 ENIAC 팀에서 긴밀히 협력했으며, 제2차 세계 대전 중 그녀가 프로그래밍하던 ENIAC 컴퓨터의 서브루틴에 대한 아이디어를 개발했습니다.[12] 그녀와 다른 ENIAC 프로그래머들은 미사일 궤적을 계산하는 것을 돕기 위해 서브루틴을 사용했습니다.[12]

골드스틴과 폰 노이만은 1948년 8월 16일에 서브루틴의 사용에 대해 논하는 논문을 썼습니다.[13]

IBM 1620, 인텔 4004인텔 8008, PIC 마이크로컨트롤러와 같은 일부 초기 컴퓨터 및 마이크로프로세서에는 전용 하드웨어 스택을 사용하여 반환 주소를 저장하는 단일 명령어 서브루틴 호출이 있습니다. 이러한 하드웨어는 몇 가지 수준의 서브루틴 네스팅만 지원하지만 재귀적 서브루틴은 지원할 수 있습니다. UNIVAC I, PDP-1IBM 1130과 같은 1960년대 중반 이전의 기계는 일반적으로 호출된 서브루틴의 첫 번째 메모리 위치에 명령어 카운터를 저장하는 호출 규칙을 사용합니다. 따라서 임의로 깊은 수준의 서브루틴 네스팅을 허용하지만 재귀적 서브루틴은 지원하지 않습니다. IBM System/360에는 저장된 명령어 카운터 값을 범용 레지스터에 배치하는 서브루틴 호출 명령어가 있었습니다. 이 명령어는 임의의 딥 서브루틴 네스팅 및 재귀 서브루틴을 지원하는 데 사용될 수 있습니다. 버로우즈 B5000[14](1961)은 서브루틴 리턴 데이터를 스택에 저장한 최초의 컴퓨터 중 하나입니다.

DEC PDP-6[15](1964)은 누적기 또는 인덱스 레지스터에 의해 주소가 지정된 스택에 반환 주소를 저장하는 서브루틴 호출 명령을 가진 최초의 누적기 기반 기계 중 하나입니다. 이후 PDP-10(1966), PDP-11(1970), VAX-11(1976) 라인이 그 뒤를 이었습니다. 이 기능은 임의의 깊은 서브루틴 네스팅과 재귀 서브루틴을 모두 지원합니다.[16]

언어지원

초기 어셈블러에서는 서브루틴 지원이 제한적이었습니다. 서브루틴은 서로 또는 메인 프로그램과 명시적으로 분리되지 않았으며, 실제로 서브루틴의 소스 코드는 다른 서브 프로그램의 소스 코드와 산재될 수 있습니다. 일부 어셈블러는 호출 및 반환 시퀀스를 생성하기 위해 미리 정의된 매크로를 제공합니다. 1960년대까지 어셈블러들은 대개 서로 연결될 수 있는 인라인 및 별도로 조립된 서브루틴 모두에 대해 훨씬 더 정교한 지원을 제공했습니다.

사용자가 작성한 서브루틴과 기능을 지원하는 최초의 프로그래밍 언어 중 하나가 FORTRAN II였습니다. IBM FORTRAN II 컴파일러는 1958년에 출시되었습니다. ALGOL 58과 다른 초기 프로그래밍 언어들도 절차적 프로그래밍을 지원했습니다.

라이브러리

이 번거로운 접근 방식에도 불구하고 서브루틴은 매우 유용하다는 것이 입증되었습니다. 그들은 많은 다른 프로그램에서 동일한 코드를 사용할 수 있도록 허용했습니다. 초기 컴퓨터에서 메모리는 매우 부족한 자원이었고 서브루틴을 사용하면 프로그램 크기를 크게 절약할 수 있었습니다.

많은 초기 컴퓨터들은 프로그램 명령어들을 펀칭된 종이 테이프에서 메모리로 로딩했습니다. 그런 다음 각 서브루틴은 메인 프로그램(또는 "메인 라인")[17] 이전 또는 이후에 로드되거나 스플라이싱된 별도의 테이프로 제공될 수 있으며, 동일한 서브루틴 테이프는 많은 다른 프로그램에서 사용될 수 있습니다. 펀칭된 카드에서 프로그램 명령을 로드하는 컴퓨터에서도 유사한 접근 방식이 사용되었습니다. 서브루틴 라이브러리라는 이름은 원래 문자 그대로의 의미에서 집합적으로 사용할 수 있도록 테이프나 카드 데크의 색인 컬렉션을 보관하는 라이브러리를 의미했습니다.

간접 점프에 의한 리턴

컴퓨터 설계자들은 결국 코드 자체를 수정할 필요를 없애기 위해 간접 점프 명령을 제공했는데, 이 명령의 피연산자는 반환 주소 자체가 아니라 반환 주소를 포함하는 변수 또는 프로세서 레지스터의 위치였습니다.

이러한 컴퓨터에서는 함수의 반환 점프를 수정하는 대신 호출 프로그램이 반환 주소를 변수에 저장하여 함수가 완료되면 미리 정의된 변수에 의해 지정된 위치로 실행되는 간접 점프를 실행합니다.

서브루틴으로 점프

또 다른 진전은 서브루틴 명령으로의 점프로, 반환 주소의 저장과 호출 점프를 결합하여 오버헤드를 크게 최소화하는 것이었습니다.

예를 들어 IBM System/360의 경우 프로시저 호출을 위해 설계된 분기 명령 BAL 또는 BALR은 컨벤션 레지스터 14에 의해 해당 명령에 지정된 프로세서 레지스터에 반환 주소를 저장합니다. 다시 돌아오려면 서브루틴이 해당 레지스터를 통해 간접 분기 명령(BR)을 실행하기만 하면 됩니다. 서브루틴이 다른 목적(예: 다른 서브루틴 호출)을 위해 해당 레지스터를 필요로 하는 경우 레지스터의 내용을 개인 메모리 위치 또는 레지스터 스택에 저장합니다.

HP 2100과 같은 시스템에서 JSB 명령은 반환 주소가 분기의 대상인 메모리 위치에 저장된 것을 제외하고는 유사한 작업을 수행합니다. 절차의 실행은 실제로 다음 메모리 위치에서 시작됩니다. 예를 들어 HP 2100 어셈블리 언어로 작성할 수 있습니다.

...JSB MYSUB (Calls subroutine MYSUB) BB ... (MYSUB가 끝나면 여기로 돌아올 것입니다.) 

메인 프로그램에서 MYSUB라는 서브루틴을 호출합니다. 서브루틴은 다음과 같이 코딩됩니다.

MYSUB NOP(MYSUB의 반환 주소에 대한 저장) AA... (MYSUB 본체의 시작)... JMP MYSUB, I (통화 프로그램으로 돌아갑니다.) 

JSB 명령어는 NEXT 명령어(namely, BB)의 주소를 피연산자(namely, MYSUB)로 지정된 위치에 배치한 후, 그 후 NEXT 위치(namely, AA = MYSUB + 1)로 분기하였습니다. 그런 다음 서브루틴은 MYSUB에 저장된 위치로 분기된 간접 점프 JMP MYSUB를 실행하여 메인 프로그램으로 돌아갈 수 있습니다.

Fortran 및 기타 언어용 컴파일러는 사용 가능할 때 이러한 지침을 쉽게 사용할 수 있습니다. 이 방법은 여러 수준의 호출을 지원했지만, 서브루틴의 반환 주소, 파라미터 및 반환 값에 고정 메모리 위치가 할당되었기 때문에 재귀적 호출을 허용하지 않았습니다.

덧붙여서, 1980년대 초반에 Lotus 1-2-3에서 스프레드시트에서 재계산 종속성을 발견하기 위해 유사한 방법이 사용되었습니다. 즉, 반송 주소를 저장하기 위해 각 셀에 위치를 예약했습니다. 원형 참조는 자연스러운 재계산 순서에 허용되지 않으므로 IBM PC와 같은 소형 컴퓨터에서는 매우 제한적이었던 메모리 스택의 공간을 예약하지 않고 트리 워크를 수행할 수 있습니다.

콜스택

함수 호출의 대부분의 최신 구현은 함수 호출 및 반환을 구현하기 위해 스택 데이터 구조의 특수한 경우인 호출 스택을 사용합니다. 각 프로시저 호출은 스택의 맨 위에 스택 프레임이라고 하는 새로운 엔트리를 생성합니다. 프로시저가 반환되면 해당 스택 프레임은 스택에서 삭제되고 해당 공간은 다른 프로시저 호출에 사용될 수 있습니다. 각 스택 프레임에는 일반적으로 절차의 매개 변수와 내부 변수 및 반환 주소가 포함된 해당 호출의 개인 데이터가 포함됩니다.

호출 시퀀스는 일반 명령어 시퀀스(RISC(Reduced Instruction Set Computing)와 VLIW(Very Long Instruction Word) 아키텍처에서 여전히 사용되는 접근 방식)로 구현할 수 있지만 1960년대 후반부터 설계된 많은 전통적인 기계에는 이러한 목적을 위한 특별한 명령어가 포함되어 있습니다.

호출 스택은 일반적으로 메모리의 연속 영역으로 구현됩니다. 스택의 맨 아래가 이 영역 내에서 가장 낮은 주소인지 가장 높은 주소인지에 관계없이 임의의 설계 선택이므로 스택이 메모리에서 앞으로 성장하거나 뒤로 성장할 수 있습니다. 그러나 많은 아키텍처가 후자를 선택했습니다.[citation needed]

일부 설계, 특히 일부 포스 구현은 두 개의 개별 스택을 사용했는데, 하나는 주로 제어 정보(예: 리턴 주소 및 루프 카운터)에 사용되었고 다른 하나는 데이터에 사용되었습니다. 전자는 콜 스택(call stack)이었고, 다른 언어 구성을 통해 프로그래머가 간접적으로만 접근할 수 있었던 반면 후자는 더 직접적으로 접근할 수 있었습니다.

스택 기반 프로시저 호출이 처음 도입되었을 때 중요한 동기는 소중한 메모리를 저장하는 것이었습니다.[citation needed] 이 방식을 사용하면 컴파일러는 각 프로시저의 개인 데이터(파라미터, 리턴 주소 및 로컬 변수)에 대해 메모리에 별도의 공간을 예약할 필요가 없습니다. 스택에는 현재 활성화된 호출(즉, 호출되었지만 아직 반환되지 않은 호출)의 개인 데이터만 포함됩니다. 보통 도서관에서 프로그램을 조립하는 방식 때문에, 특정 순간에 소수의 기능만 활성화되는 수천 개의 기능을 포함하는 프로그램을 찾는 것은 드문 일이 아니었습니다.[citation needed] 이러한 프로그램의 경우 호출 스택 메커니즘을 통해 상당한 양의 메모리를 절약할 수 있습니다. 실제로 콜 스택 메커니즘은 메모리 자동 관리를 위한 가장 빠르고 간단한 방법이라고 볼 수 있습니다.

그러나 호출 스택 방법의 또 다른 장점은 동일한 프로시저에 대한 각 중첩 호출이 개인 데이터의 개별 인스턴스를 얻기 때문에 재귀 함수 호출을 허용한다는 것입니다.

멀티 스레드 환경에서는 일반적으로 스택이 하나 이상 존재합니다.[18] 코루틴 또는 게으른 평가를 완벽하게 지원하는 환경은 스택 이외의 데이터 구조를 사용하여 활성화 레코드를 저장할 수 있습니다.

지연적층

콜 스택 메커니즘의 한 가지 단점은 프로시저 콜의 비용 증가와 그에 상응하는 수익입니다.[clarification needed] 추가 비용에는 스택 포인터의 증가 및 감소(일부 아키텍처에서는 스택 오버플로 검사), 절대 주소 대신 프레임 관련 주소를 통해 로컬 변수 및 매개 변수에 액세스하는 것이 포함됩니다. 비용은 실행 시간 증가, 프로세서 복잡성 증가, 또는 둘 다에서 실현될 수 있습니다.

오버헤드는 잎 시술 또는 잎 기능에서 가장 명확하고 반대가 되며, 어떤 시술도 스스로 호출하지 않고 되돌아옵니다.[19][20][21] 이러한 오버헤드를 줄이기 위해, 많은 최신 컴파일러들은 콜 스택이 정말 필요할 때까지 콜 스택의 사용을 지연시키려 합니다.[citation needed] 예를 들어, 프로시저 P의 호출은 호출된 프로시저의 리턴 어드레스 및 파라미터를 특정 프로세서 레지스터에 저장하고, 간단한 점프에 의해 프로시저 본체에 제어를 전달할 수 있습니다. 절차 P가 다른 호출을 하지 않고 돌아오면 호출 스택은 전혀 사용되지 않습니다. P가 다른 프로시저 Q를 호출해야 하는 경우, P는 콜 스택을 사용하여 Q 반환 후 필요한 레지스터(예: 반환 주소)의 내용을 저장합니다.

특징들

일반적으로 호출 가능한 유닛은 첫 번째 명령어에서 시작하여 내부 로직을 통해 지시된 경우를 제외하고 순차적으로 실행하는 명령어 목록입니다. 프로그램 실행 중에 여러 번 호출(호출)할 수 있습니다. 호출 명령어가 제어권을 반환하면 다음 명령어에서 실행이 계속됩니다.

구현

호출 가능한 단위 기술의 구현 특징은 시간이 지남에 따라 진화했으며 상황에 따라 다릅니다. 이 섹션에서는 다양한 공통 구현의 특징에 대해 설명합니다.

일반적 특성

대부분의 현대 프로그래밍 언어는 다음과 같은 기능에 접근하기 위한 구문을 포함하여 함수를 정의하고 호출하는 기능을 제공합니다.

  • 기능의 구현을 프로그램의 나머지 부분과 구분합니다.
  • 함수에 식별자, 이름 할당
  • 각각에 대한 이름 및 데이터 유형을 사용하여 형식 매개변수 정의
  • 반환 값에 데이터 유형 할당(있는 경우)
  • 함수 본문에서 반환 값 지정
  • 함수 호출
  • 호출된 함수의 실제 파라미터에 해당하는 실제 파라미터 제공
  • 통화 시점에서 발신자에게 제어권 반환
  • 발신자의 반환 값 소비
  • 호출에 의해 반환된 값 처리
  • 변수에 대한 개인 이름 지정 범위 제공
  • 함수 내부에서 접근 가능한 함수 외부의 변수를 식별합니다.
  • 함수에서 예외적인 조건을 전파하고 호출 컨텍스트에서 처리합니다.
  • 모듈, 라이브러리, 개체 또는 클래스와 같은 컨테이너에 기능 패키지

명명

파스칼, 포트란, 에이다 그리고 베이직많은 방언들과 같은 일부 언어들은 값(함수 또는 하위 프로그램)을 반환하는 호출 가능한 단위와 반환하지 않는 단위(서브루틴 또는 프로시저)에 대해 다른 이름을 사용합니다. C, C++, C#, 리스프와 같은 다른 언어들은 호출 가능한 단위인 함수에 하나의 이름만을 사용합니다. C-패밀리 언어는 키워드를 사용합니다. void 반환 값이 없음을 나타냅니다.

호출 구문

값을 반환하도록 선언된 경우 반환 값을 소비하기 위해 호출이 에 포함될 수 있습니다. 예를 들어 제곱근 호출 가능 단위는 다음과 같이 호출할 수 있습니다. y = sqrt(x).

값을 반환하지 않는 호출 가능한 단위를 다음과 같은 독립 실행형 문이라고 합니다. print("hello"). 이 구문은 값을 반환하는 호출 가능한 단위에도 사용할 수 있지만 반환 값은 무시됩니다.

일부 오래된 언어는 반환 값을 소비하지 않는 호출에 대해 키워드를 필요로 합니다. CALL print("hello").

매개변수

특히 현대 언어에서 대부분의 구현은 호출 가능한 매개 변수를 공식 매개 변수로 선언하는 매개 변수를 지원합니다. 호출자는 일치시키기 위해 실제 매개 변수, 즉 인수를 전달합니다. 서로 다른 프로그래밍 언어는 인수를 통과하기 위한 서로 다른 규칙을 제공합니다.

관습 묘사 사용처
값으로 아그먼트 사본이 전달됩니다. Pascal, Delphi, Simula, CPL, PL/M, Modula, Oberon, Ada 등 Algol 60 이후의 대부분의 Algol 유사 언어에서 기본값으로 사용됩니다.
참고로 인수에 대한 참조가 통과되었습니다. 일반적으로 해당 주소입니다. Algol 68, Pascal, Delphi, Simula, CPL, PL/M, Modula, Oberon, Ada 등 Algol 60 이후의 대부분의 Algol 유사 언어에서 선택 가능
결과적으로 통화 중에 계산된 값이 반환 시 인수에 복사됩니다. Ada OUT 파라미터
가치 result으로 인수의 복사본이 전달되고 통화 중에 계산된 값이 반환 시 인수에 복사됩니다. Algol, Swift in-out 파라미터
이름으로 매크로처럼 – 매개 변수를 평가되지 않은 인수 식으로 바꾼 다음 호출자가 매개 변수를 사용할 때마다 호출자의 컨텍스트에서 인수를 평가합니다. 알골, 스칼라
일정한 값으로 모수가 상수로 취급된다는 점을 제외하고는 기준값과 같습니다. PL/I Nonassignable 파라미터, Adain 파라미터

반환가액

BASIC과 같은 일부 언어에서 호출기는 값을 반환하는 호출기와 반환하지 않는 호출기의 구문(즉, 키워드)이 다릅니다. 다른 언어에서는 구문이 상관없이 동일합니다. 이러한 언어 중 일부에서는 추가 키워드를 사용하여 반환 값 없음을 선언합니다. 예를 들어, void in C, C++ and C#. Python과 같은 일부 언어에서 차이점은 본문에 값이 포함된 반환문이 포함되어 있는지 여부이며, 특정 호출기는 제어 흐름에 따라 값이 포함되거나 포함되지 않은 상태로 반환될 수 있습니다.

부작용

많은 맥락에서 호출기는 전달된 데이터 또는 전역 데이터 수정, 주변 장치 읽기 또는 쓰기, 파일 액세스, 프로그램 또는 기계 중지 또는 프로그램 실행 일시 중지와 같은 부작용 동작을 가질 수 있습니다.

해스켈과 같은 엄밀하게 기능하는 프로그래밍 언어에서 함수는 부작용이 없을 수 있으며, 이는 프로그램의 상태를 변경할 수 없다는 것을 의미합니다. 함수는 항상 동일한 입력에 대해 동일한 결과를 반환합니다. 이러한 언어는 일반적으로 값을 반환하는 함수만 지원하는데, 이는 반환 값이나 부작용이 없는 함수에는 값이 없기 때문입니다.

지역 변수

대부분의 컨텍스트는 중간 값을 유지할 수 있는 호출기가 소유한 메모리인 로컬 변수를 지원합니다. 이러한 변수는 일반적으로 반환 주소와 같은 다른 정보와 함께 통화 스택의 통화 활성화 레코드에 저장됩니다.

중첩 호출 – 재귀

언어에서 지원하는 경우 호출 가능한 자체를 호출하여 동일한 호출 가능한 다른 중첩 실행이 실행되는 동안 실행이 중지될 수 있습니다. 재귀는 일부 복잡한 알고리즘을 단순화하고 복잡한 문제를 분해하는 데 유용한 수단입니다. 재귀 언어는 각 호출에 대한 로컬 변수의 새 복사본을 제공합니다. 프로그래머가 재귀 호출자가 로컬을 사용하는 대신 동일한 변수를 사용하기를 원하는 경우 일반적으로 정적 또는 전역과 같은 공유 컨텍스트에서 호출자를 선언합니다.

ALGOL, PL/IC로 거슬러 올라가는 언어와 현대 언어는 거의 변함없이 각 호출에 대한 활성화 레코드를 제공하기 위해 명령 세트에 의해 지원되는 호출 스택을 사용합니다. 이렇게 하면 중첩된 호출은 일시 중단된 호출 변수에 영향을 주지 않고 로컬 변수를 수정할 수 있습니다.

재귀를 사용하면 수학적 귀납법과 재귀적 분할 정복 알고리즘에 의해 정의된 기능을 직접 구현할 수 있습니다. 다음은 피보나치 수를 찾기 위한 C/C++의 재귀 함수의 예입니다.

인트의 피브(인트의 n) {   한다면 (n <= 1) {     돌아가다 n;   }   돌아가다 피브(n - 1) + 피브(n - 2); } 

포트란과 같은 초기 언어는 각 호출 가능한 변수에 대해 하나의 변수 집합과 반환 주소만 할당되었기 때문에 처음에는 재귀를 지원하지 않았습니다.[22] 초기 컴퓨터 명령어 세트는 반환 주소와 변수를 스택에 저장하는 것을 어렵게 만들었습니다. 인덱스 레지스터 또는 범용 레지스터(예: CDC 6000 시리즈, PDP-6, GE 635, System/360, UNIVAC 1100 시리즈)를 가진 기계는 이러한 레지스터 중 하나를 스택 포인터로 사용할 수 있습니다.

중첩 범위

Pascal, PL/I 및 Ada와 같은 일부 언어는 호출 가능한 중첩 범위, 즉 nested 함수를 지원하므로 내부 호출 가능한 것은 외부 호출 가능한 것만 호출할 수 있습니다. 내부 호출기는 외부의 로컬 변수에 액세스할 수 있습니다.

재진입

동일한 호출기의 다른 실행이 이미 진행 중인 경우에도 호출기를 제대로 실행할 수 있는 경우 해당 호출기는 재진입(reentrant)이라고 합니다. 재귀 호출기는 다시 입력해야 합니다. 재진입 호출기는 여러 스레드가 서로 간섭할 염려 없이 동일한 호출기를 호출할 수 있기 때문에 다중 스레드 상황에서도 유용합니다. IBM CICS 트랜잭션 처리 시스템에서 준 재진입은 많은 스레드가 공유하는 응용 프로그램에 대해 약간 덜 제한적이지만 유사한 요구 사항이었습니다.

과부하

일부 언어는 오버로드를 지원합니다. 동일한 범위에서 동일한 이름을 가진 여러 호출기를 허용하지만 다른 유형의 입력에서 작동합니다. 실수, 복소수, 행렬 입력에 적용되는 제곱근 함수를 생각해 보십시오. 입력 유형별 알고리즘이 다르며, 반환 값이 다를 수 있습니다. sqrt와 같은 이름을 가진 세 개의 개별 호출기를 작성함으로써 sqrt_real, sqrt_complex, qrt_matrix와 같이 더 길고 복잡한 이름을 제공하는 대신 상대적으로 이해하기 쉽고 기억하기 쉬운 이름을 가지고 있기 때문에 결과적인 코드를 작성하고 유지하기가 더 쉬울 수 있습니다.

강력한 타이핑을 지원하는 많은 언어에서 과부하가 지원됩니다. 컴파일러는 종종 입력 인수의 유형에 따라 호출할 오버로드를 선택하거나 입력 인수가 오버로드를 선택하지 않으면 실패합니다. 오래되고 약한 유형의 언어는 일반적으로 과부하를 지원하지 않습니다.

다음은 C++에서 오버로드된 두 기능의 예입니다. Area 다양한 유형을 허용합니다.

// 높이와 너비로 정의된 직사각형의 면적을 반환합니다. 두 배의 지역(두 배의 h, 두 배의 w) { 돌아가다 h * w; }  // 반지름으로 정의된 원의 면적을 반환합니다. 두 배의 지역(두 배의 r) { 돌아가다 r * r * 3.14; }  인트의 주된() {   두 배의 직사각형_면적 = 지역(3, 4);   두 배의 circle_area = 지역(5); } 

PL/I have the GENERIC 다른 유형의 인수로 호출되는 항목 참조 집합의 일반 이름을 정의하는 속성입니다. 예:

DELARCED gen_name GENERIC(이름은 Fixed Binary(고정 이진), flame은 FLOAT(플로트), pathname은 그렇지 않으면),

각 항목에 대해 여러 인수 정의를 지정할 수 있습니다. "gen_name"에 호출하면 인수가 FIXED BINICAL인 경우 "name"에 호출됩니다. "float인 경우 "flame" 등에 호출됩니다. 인수가 일치하는 경우 "pathname" 선택사항 중 하나도 호출되지 않습니다.

휴무

클로저는 클로저가 생성된 환경에서 캡처된 일부 변수의 호출 가능한 플러스 값입니다. 폐쇄는 존 매카시가 도입한 리스프 프로그래밍 언어의 주목할 만한 특징이었습니다. 구현에 따라 폐쇄가 부작용의 메커니즘으로 작용할 수 있습니다.

예외보고

호출 가능한 사람은 해피 경로 동작 외에도 실행 중에 발생한 예외적인 상태에 대해 호출자에게 알려야 할 수도 있습니다.

대부분의 현대 언어는 예외를 지원하며 예외 처리기가 이 상태를 처리하는 것을 발견할 때까지 호출 스택을 터뜨리는 예외적인 제어 흐름을 허용합니다.

예외를 지원하지 않는 언어는 반환 값을 사용하여 통화의 성공 또는 실패를 나타낼 수 있습니다. 또 다른 방법은 성공 표시를 위해 글로벌 변수와 같은 잘 알려진 위치를 사용하는 것입니다. 호출자는 값을 기록하고 호출자는 호출 후 값을 읽습니다.

서브루틴에서 반환 코드가 예상되는 IBM System/360에서는 반환 값이 종종 4의 배수로 설계되어 추가 조건부 테스트를 피하기 위해 호출 명령 직후에 위치하는 분기 테이블에 직접 분기 테이블 인덱스로 사용할 수 있어 효율성이 더욱 향상되었습니다. 시스템/360 어셈블리 언어로 다음과 같이 쓸 수 있습니다.

           BAL 14, SUBRTN01은 서브루틴으로 이동하여 R14 B 테이블(15)에 반환 주소를 저장합니다. reg 15에서 반환된 값을 사용하여 분기 테이블을 인덱싱하고, * 적절한 분기 instr로 분기합니다. 표 BOK 반환 코드 =00 GOOD } BAD 반환 코드 =04 잘못된 입력 } 분기 테이블 B ERROR 반환 코드 =08 예기치 않은 조건 } 

콜오버헤드

통화에는 런타임 오버헤드가 있습니다. 이 오버헤드는 다음을 포함할 수 있지만 이에 제한되지는 않습니다.

  • 콜 스택 스토리지 할당 및 회수
  • 프로세서 레지스터 저장 및 복원
  • 입력 변수 복사 중
  • 통화 후 값을 발신자의 컨텍스트로 복사
  • 반송코드 자동시험
  • 예외 처리
  • 객체 지향 언어로 가상 메소드를 위한 등의 디스패치

통화의 런타임 비용을 최소화하기 위해 다양한 기술이 사용됩니다.

컴파일러 최적화

통화 오버헤드를 최소화하기 위한 일부 최적화는 간단해 보이지만 통화에 부작용이 있는 경우에는 사용할 수 없습니다. 예를 들어, 식에서 (f(x)-1)/(f(x)+1), 직능 f 두 호출이 서로 다른 결과를 반환할 수 있으므로 두 번 사용된 값으로 한 번만 호출할 수 없습니다. 또한 분할 연산자의 피연산자의 평가 순서를 정의하는 몇 가지 언어에서 다음과 같은 값이 있습니다. x 첫 번째 호출이 변경되었을 수 있으므로 두 번째 호출 전에 다시 가져와야 합니다. 콜러블이 부작용을 갖는지 여부를 판단하는 것은 어렵습니다. 사실 라이스의 정리결정할 수 없습니다. 따라서, 이러한 최적화는 순수하게 기능적인 프로그래밍 언어에서는 안전하지만, 기능적인 언어에 국한되지 않는 컴파일러는 일반적으로 최악의 경우를 가정하는데, 모든 호출기는 부작용을 가질 수 있습니다.

인라이닝

인라이닝은 특정 통화에 대한 호출을 제거합니다. 컴파일러는 각 호출을 컴파일된 호출 코드로 대체합니다. 이렇게 하면 호출 오버헤드를 피할 수 있을 뿐만 아니라 컴파일러가 호출 시 컨텍스트와 인수를 고려하여 호출자의 코드를 보다 효과적으로 최적화할 수 있습니다. 그러나 인라이닝은 한 번만 호출하거나 한 줄처럼 본체가 매우 짧은 경우를 제외하고는 일반적으로 컴파일된 코드 크기를 증가시킵니다.

공유.

호출 가능은 프로그램 내에서 정의하거나 여러 프로그램에서 사용할 수 있는 라이브러리에서 별도로 정의할 수 있습니다.

상호운용성

컴파일러는 잘 정의된 호출 규칙에 따라 호출 및 반환 문을 기계 명령어로 변환합니다. 동일한 컴파일러 또는 호환 컴파일러에 의해 컴파일된 코드의 경우 함수를 호출하는 프로그램과 별도로 컴파일할 수 있습니다. 호출 및 반환 문에 해당하는 명령 시퀀스를 절차의 프롤로그 에필로그라고 합니다.

내장 기능

내장 함수 또는 내장 함수 또는 고유 함수컴파일러가 컴파일 시 코드를 생성하거나 다른 함수가 아닌 다른 방식으로 제공하는 함수입니다.[23] 내장된 함수는 프로그래밍 언어에 내장되어 있기 때문에 다른 함수처럼 정의할 필요가 없습니다.[24]

프로그래밍

절충안

이점

프로그램을 기능으로 분할하는 장점은 다음과 같습니다.

  • 다양한 프로그래머 또는 프로젝트의 다양한 단계에서 큰 프로그래밍 작업을 분할하는 것
  • 코드 블록을 함수 호출로 대체하여 코드의 가독성을 향상시키는 것. 여기서 설명 함수 이름은 코드 블록을 설명하는 역할을 합니다. 따라서 기능을 재사용하려는 의도가 아니더라도 호출 코드를 간결하고 읽을 수 있습니다.
  • 추적성을 향상시키는 것(즉, 대부분의 언어는 관련된 함수의 이름과 파일 이름 및 줄 번호와 같은 더 많은 정보를 포함하는 호출 추적을 얻을 수 있는 방법을 제공합니다); 코드를 함수로 분해하지 않음으로써 디버깅이 심각하게 손상될 수 있습니다.

단점들

인라인 코드를 사용하는 것과 비교하여 함수를 호출하면 호출 메커니즘에서 약간의 계산 오버헤드가 발생합니다.[citation needed]

함수는 일반적으로 함수(함수 프롤로그 에필로그일반적으로 범용 레지스터 및 반환 주소를 최소로 저장)에 대한 표준 하우스키핑 코드를 필요로 합니다.

관습

호출기와 관련하여 많은 프로그래밍 규칙이 개발되었습니다.

명명과 관련하여 많은 개발자들은 호출 가능한 단어를 특정 작업을 수행할 때 동사로 시작하고, 조회할 때 형용사로, 변수를 대체할 때 명사로 명명합니다.

일부 프로그래머는 호출기가 정확히 하나의 작업을 수행해야 하며, 하나 이상의 작업을 수행하는 경우 여러 호출기로 분할해야 한다고 제안합니다. 이들은 호출기가 소프트웨어 유지 보수의 핵심 구성 요소이며, 프로그램에서 이들의 역할은 별개로 유지되어야 한다고 주장합니다.

모듈식 프로그래밍을 지지하는 사람들은 각 호출기가 코드베이스의 나머지 부분에 최소한의 의존성을 가져야 한다고 주장합니다. 예를 들어, 전역 변수를 사용하는 모든 호출기 간에 결합을 추가하기 때문에 전역 변수를 사용하는 것은 일반적으로 현명하지 못한 것으로 간주됩니다. 이러한 커플링이 필요하지 않은 경우, 대신 전달된 매개 변수를 수락하도록 호출 가능한 요소를 리팩터하는 것이 좋습니다.

초기 베이직

초기 BASIC 변형은 각 라인이 실행을 위해 라인을 순서화하고, 호출 가능한 코드의 분리를 제공하지 않으며, 인수를 전달하거나 값을 반환하는 메커니즘이 없으며, 모든 변수가 전역적이어야 합니다. 명령을 제공합니다. GOSUB 여기서 subsub procedure, subprocedure 또는 subroutine의 줄임말입니다. 컨트롤은 지정된 라인 번호로 점프한 다음 반환 시 다음 라인에서 계속 진행됩니다.

10 REMA 기본 프로그램 20 고섭 100 30 에 가다 20 100 입력  나야. A 번호; N 110 프린트  광장 뿌리 OF; N;  120 프린트 IS; SQRT(N) 130 돌아가다 

이 코드는 사용자에게 숫자를 입력하도록 반복적으로 요청하고 값의 제곱근을 보고합니다. 100-130번은 통화 가능합니다.

스몰 베이직

텍스트 기반 언어로 프로그래밍하는 방법을 먼저 생각하는 학생을 대상으로 하는 마이크로소프트 스몰 베이직에서는 호출 가능한 단위를 서브루틴이라고 합니다.Sub 키워드는 서브루틴의 시작을 나타내며 이름 식별자가 뒤따릅니다. 다음 줄은 다음으로 끝나는 본문입니다. EndSub 키워드.

후보선수 안부 전해주세요.     텍스트 창.쓰기 선("안녕하세요!") EndSub 

이를 다음과 같이 부를 수 있습니다. SayHello(). [26]

비주얼 베이직

최신 제품군VB6를 포함한 VB(Visual Basic)의 이후 버전에서는 호출 가능한 단위 개념에 대해 절차라는 용어가 사용됩니다. 키워드. Sub 값을 반환하지 않는 데 사용되며, Function 값을 돌려줍니다. 수업의 맥락에서 사용되는 경우 절차는 하나의 방법입니다.

각 매개 변수에는 지정할 수 있는 데이터 유형이 있지만 지정할 수 없는 경우 기본값은 다음과 같습니다. Object 를 기반으로 한 이후 버전의 경우.VB6용 NET변형.[28]

VB는 키워드를 통해 값별참조로 매개 변수 전달 규칙을 지원합니다. ByVal 그리고. ByRef,각각 다음과 같다. ~하지 않는 한 ByRef 지정하면 인수가 통과됩니다. ByVal.그러므로, ByVal 명시적으로 지정된 경우는 거의 없습니다.

숫자와 같은 단순한 유형의 경우 이러한 규칙은 비교적 명확합니다. 통과 ByRef 프로시저가 통과하는 동안 통과된 변수를 수정할 수 있도록 허용합니다. ByVal 하지 않다. 개체의 경우 개체가 항상 참조로 취급되기 때문에 의미론은 프로그래머를 혼란스럽게 할 수 있습니다. 개체 전달하기 ByVal 개체의 상태가 아닌 참조를 복사합니다. 호출된 프로시저는 메서드를 통해 개체의 상태를 수정할 수 있지만 실제 매개 변수의 개체 참조는 수정할 수 없습니다.

후보선수 어떻게 좀 해봐.()     ' 여기에 일부 코드 끝. 후보선수 

는 값을 반환하지 않으며 다음과 같이 독립 실행형이라고 불러야 합니다. DoSomething

기능. 다섯 개를 주세요.() ~하듯이 정수     다섯 개를 주세요.= 5 끝. 기능. 

이렇게 하면 값 5가 반환되며, 호출은 다음과 같은 식의 일부가 될 수 있습니다. y = x + GiveMeFive()

후보선수 둘 추가(기준 intValue ~하듯이 정수)     intValue = intValue + 2 끝. 후보선수 

이로 인해 참조로 전달된 변수가 수정되고 변수가 호출될 수 있는 부작용이 발생합니다. v 맘에 들다 AddTwo(v). 통화 전에 v를 5로 주면 7 이후가 될 것입니다.

C와 C++

C와 C++에서 호출 가능한 단위를 함수라고 합니다. 함수 정의는 반환되는 값 유형의 이름으로 시작합니다. void 값을 반환하지 않음을 나타냅니다. 함수 이름, 괄호 안의 형식적 인수, 교정기의 바디 라인이 뒤를 이습니다.

C++에서는 클래스에서 선언된 함수(비정적인 것으로)를 멤버 함수 또는 메서드라고 합니다. 클래스 밖의 함수는 멤버 함수와 구분하기 위해 자유 함수라고 할 수 있습니다.

공허한 어떻게 좀 해봐.() {     /* 어떤 코드 */ } 

이 함수는 값을 반환하지 않으며 다음과 같이 항상 독립 실행형이라고 합니다. doSomething()

인트의 다섯 개를 주세요.() {     돌아가다 5; } 

이 함수는 정수 값 5를 반환합니다. 통화는 독립형일 수도 있고 다음과 같은 표현일 수도 있습니다. y = x + giveMeFive()

공허한 둘 더하기(인트의 *파이) {     *파이 += 2; } 

이 함수는 부작용이 있습니다 – 주소를 통해 전달된 값을 입력 값에 2를 더한 값으로 수정합니다. 변수에 대해 호출할 수 있습니다. v ~하듯이 addTwo(&v) 여기서 앰퍼샌드(&)는 컴파일러에게 변수의 주소를 전달하도록 지시합니다. 통화 전에 v를 5로 주면 7 이후가 될 것입니다.

공허한 둘 더하기(인트의& i) {     i += 2; } 

이 함수는 C++가 필요합니다. C로 컴파일되지 않습니다. 앞의 예와 동일한 동작을 갖지만 주소를 전달하는 것이 아니라 참조로 실제 매개 변수를 전달합니다. 다음과 같은 전화. addTwo(v) 는 앰퍼를 포함하지 않으며 컴파일러는 호출에서 구문 없이 참조를 통과하는 것을 처리하기 때문입니다.

PL/I

PL/I에서 호출된 프로시저는 문자열 길이 및 배열 경계와 같은 인수에 대한 정보를 제공하는 디스크립터를 전달할 수 있습니다. 이를 통해 절차를 보다 일반적으로 수행할 수 있으며 프로그래머가 이러한 정보를 전달할 필요가 없습니다. 기본적으로 PL/I는 인수를 참조로 전달합니다. 2차원 배열의 각 요소의 부호를 변경하는 (사소한) 함수는 다음과 같이 보일 수 있습니다.

change_sign: procedure(array); declarate (*,*) float; array = -array; change_sign 종료; 

이것은 다음과 같이 다양한 배열에서 호출될 수 있습니다.

/* -5 ~ +10 및 3 ~ 9 */ array1(-5:10, 3:9) float 선언; /* 1 ~ 16 및 1 ~ 16 */ array2(16, 16) 플로트 선언; call change_sign(array1); call change_sign(array2); 

파이썬

파이썬에서 키워드는 def 함수 정의의 시작을 나타냅니다. 함수 본문의 문은 후속 행에서 들여쓰기된 것처럼 따르고 파일의 첫 번째 행 또는 끝과 동일하게 들여쓰기된 행에서 끝납니다.[30]

디프 형식_greeting(이름.):     돌아가다 "어서오세요" + 이름. 디프 greet_martin():     활자로 인쇄하다(형식_greeting(마틴)) 

첫 번째 기능은 발신자가 전달한 이름이 포함된 인사말 텍스트를 반환합니다. 두 번째 함수는 첫 번째 함수를 호출하고 다음과 같이 부릅니다. greet_martin() 콘솔에 "어서와 마틴"이라고 적습니다.

참고 항목

참고문헌

  1. ^ "Terminology Glossary". nist.gov. NIST. Retrieved 9 February 2024. Callable unit: (Of a software program or logical design) Function, method, operation, subroutine, procedure, or analogous structural unit that appears within a module.
  2. ^ Donald E. Knuth (1997). The Art of Computer Programming, Volume I: Fundamental Algorithms. Addison-Wesley. ISBN 0-201-89683-4.
  3. ^ O.-J. Dahl; E. W. Dijkstra; C. A. R. Hoare (1972). Structured Programming. Academic Press. ISBN 0-12-200550-3.
  4. ^ Subrata Dasgupta (7 January 2014). It Began with Babbage: The Genesis of Computer Science. Oxford University Press. pp. 155–. ISBN 978-0-19-930943-6.
  5. ^ a b Mauchly, J.W. (1982). "Preparation of Problems for EDVAC-Type Machines". In Randell, Brian (ed.). The Origins of Digital Computers. Springer. pp. 393–397. doi:10.1007/978-3-642-61812-3_31. ISBN 978-3-642-61814-7.
  6. ^ Wheeler, D. J. (1952). "The use of sub-routines in programmes" (PDF). Proceedings of the 1952 ACM national meeting (Pittsburgh) on - ACM '52. p. 235. doi:10.1145/609784.609816.
  7. ^ Wilkes, M. V.; Wheeler, D. J.; Gill, S. (1951). Preparation of Programs for an Electronic Digital Computer. Addison-Wesley.
  8. ^ Dainith, John (2004). ""open subroutine." A Dictionary of Computing". Encyclopedia.com. Retrieved 14 January 2013.
  9. ^ Turing, Alan M. (1945), Report by Dr. A.M. Turing on proposals for the development of an Automatic Computing Engine (ACE): Submitted to the Executive Committee of the NPL in February 1946 에 전재된.
  10. ^ Turing, Alan Mathison (19 March 1946) [1945], Proposals for Development in the Mathematics Division of an Automatic Computing Engine (ACE) (NB. 1946-03-19일 국립물리연구소(Great Britain) 집행위원회에 상정)
  11. ^ Carpenter, Brian Edward; Doran, Robert William (1 January 1977) [October 1975]. "The other Turing machine". The Computer Journal. 20 (3): 269–279. doi:10.1093/comjnl/20.3.269. (11페이지)
  12. ^ a b Isaacson, Walter (18 September 2014). "Walter Isaacson on the Women of ENIAC". Fortune. Archived from the original on 12 December 2018. Retrieved 14 December 2018.
  13. ^ Herman H. Goldstine; John von Neumann (1947). "Part II, Volume I-3, Planning and Coding of Problems for an Electronic Computing Instrument" (PDF). Report on the Mathematical and Logical aspects of an Electronic Computing Instrument (Technical report). (해당 페이지는 pdf 163쪽 참조)
  14. ^ The Operational Characteristics of the Processors for the Burroughs B5000 (PDF). Revision A. Burroughs Corporation. 1963. 5000-21005. Retrieved 8 February 2024.
  15. ^ "Push-Down Instructions" (PDF). Programmed Data Processor 6 - Handbook (PDF). p. 37. Retrieved 8 February 2024.
  16. ^ 가이 루이스 스틸 주니어. 인공지능 메모 443. '비싼 프로시저 콜' 신화를 해체하거나, 프로시저 구현이 유해하다고 간주됨' 다. 프로시저 호출이 평판이 나쁜 이유"라고 설명했습니다.
  17. ^ Frank, Thomas S. (1983). Introduction to the PDP-11 and Its Assembly Language. Prentice-Hall software series. Prentice-Hall. p. 195. ISBN 9780134917047. Retrieved 6 July 2016. We could supply our assembling clerk with copies of the source code for all of our useful subroutines and then when presenting him with a mainline program for assembly, tell him which subroutines will be called in the mainline [...]
  18. ^ Buttlar, Dick; Farrell, Jacqueline; Nichols, Bradford (1996). PThreads Programming: A POSIX Standard for Better Multiprocessing. "O'Reilly Media, Inc.". pp. 2–5. ISBN 978-1-4493-6475-5. OCLC 1036778036.
  19. ^ "ARM Information Center". Infocenter.arm.com. Retrieved 29 September 2013.
  20. ^ "x64 stack usage". Microsoft Docs. Microsoft. Retrieved 5 August 2019.
  21. ^ "Function Types". Msdn.microsoft.com. Retrieved 29 September 2013.
  22. ^ Verhoeff, Tom (2018). "A Master Class on Recursion". In Böckenhauer, Hans-Joachim; Komm, Dennis; Unger, Walter (eds.). Adventures Between Lower Bounds and Higher Altitudes: Essays Dedicated to Juraj Hromkovič on the Occasion of His 60th Birthday. Springer. p. 616. ISBN 978-3-319-98355-4. OCLC 1050567095.
  23. ^ "Built-in functions". ibm.com. Retrieved 25 December 2023.
  24. ^ Study Material Python. April 2023. p. 87. Retrieved 25 December 2023.
  25. ^ "Small Basic". Small Basic. Retrieved 8 February 2024.
  26. ^ "Small Basic Getting Started Guide: Chapter 9: Subroutines". Microsoft.
  27. ^ "Procedures in Visual Basic". Microsoft Learn. Retrieved 8 February 2024.
  28. ^ "Dim statement (Visual Basic)". Microsoft Learn. Retrieved 8 February 2024.
  29. ^ "what is meant by a free function".
  30. ^ "4. More Control Flow Tools — Python 3.9.7 documentation".