-
1.1.5 함수 적용의 치환 모형
인터프리터는 주어진 함수 적용을 평가하기 위해 먼저 함수의 적용 요소들을 먼저 평가한 수 함수의 인수들에 적용합니다. 원시 함수의 적용은 인터프리터 또는 라이브러리가 처리한다고 간주할 수 있습니다. 복합 함수의 적용 과정을 살펴볼까요?
- 하나의 복합 함수를 인수들에 적용하기 위해 함수의 각 매개변수를 해당 인수로 치환해서 함수의 반환 표현식을 평가한다.
다음 함수 적용을 예로 적용과정을 살펴보겠습니다.
f(5)
f의 반환 표현식은 아래와 같습니다.
sum_of_squares(a + 1, a * 2);
이 반환 표현식에 있는 매개변수 a를 인수 5로 치환하면 다음과 같습니다.
sum_of_squares(5 + 1, 5 * 2);
함수 표현식 sum_of_squares와 인수 두 개의 적용을 평가하는 문제로 축소 할 수 있습니다. 이 함수의 적용 평가에는 세개의 부분 문제가 관여하는데요. 함수 표현식을 평가해서 두 인수에 적용할 함수들을 구해야 하고, 두 인수 표현식을 평가해서 두 인수를 구해야 합니다. 함수 표현식 sum_of_squares가 지칭하는것은 sum_of_squares라는 이름으로 선언한 함수입니다. 그리고 5+1은 6이고 5*2는 10이라고 할수 있습니다.
이제 sum_of_squares함수를 6과 10에 적용하면 됩니다. 두 값을 sum_of_squares본문의 반환 표현식에 있는 매개변수 x와 y에 대입하면 반환 표현식은 다음과 같아집니다.
square(6) + square(6)
같은 방식으로 각 인수를 square 함수의 반환 표현식으로 대입하면 아래와 같습니다.
(6 * 6) + (10 * 10)
곱셈이 처리되고 덧셈이 처리되면 136의 최종값이 나옵니다. 이러한 과정을 함수적용의 치환 모델(substitution model: 또는 대입 모델)이라고 하는데, 이것을 함수 적용의 의미를 결정하는 모델로 봐도 좋습니다. 하지만 두 가지를 유의해야 하는데요.
- 치환 모델은 함수 적용을 이해하기 위한 수단으로만 사용되어야 하며 인터프리터가 이런 식으로 동작한다는 뜻은 아닙니다. 일반적으로 인터프리터들이 함수 텍스트의 매개 변수를 치환하는 식으로 함수 적용을 평가하지는 않습니다. 실제 인터프리터들은 매개변수들에 대한 지역환경을 지용 해서 치환을 처리합니다.
- 이런 치환 모형은 인터프리터의 다양한 모양 중에 하나입니다. 간단한 것에서부터 점점 더 복잡한 것의 순으로 제시되며 이 치환 모델은 그런 모델 중 첫 번째로 평가 과정을 형식적으로 고찰하는데 익숙해지기 위한 모형입니다. 일반적으로 과학과 공학에서 어떤 현상을 모델링할 때는 먼저 단순하고 불완전한 모델로 시작합니다. 대상을 좀 더 상세하게 조사하면 그런 단순한 모형으로는 관측된 사실들을 제대로 서술할 수 없게 되며 그런 단순한 모델을 좀 더 정련된 모델로 대체해야 합니다.
인수 우선 평가 대 정상 순서 평가
인터프리터는 함수의 인수 표현식들을 먼저 평가하고 그 결과로 얻은 함수를 인수에 적용합니다. 하지만 이것 말고도 평가를 수행하는 방식이 있는데. 인수의 값이 실제로 필요해질 때까지 인수 표현식의 평가를 미루는 평가 모델도 있습니다. 그런 평가 모델에서는 그냥 인수 표현식들을 해당 매개변수들에 대입해 놓기만 하고, 평가 과정이 진행돼서 연산자들과 원시 함수만 관여하는 표현식을 평가할 때가 되면 비로소 그 인수 표현식들을 평가합니다. 예를 들어 다음 함수 적용을 그런 식으로 평가한다고 하면
f(5)
이 함수의 적용은 다음과 같이 전개가 됩니다.
sum_of_squares(5 + 1, 5 * 2) square(5 + 1) + square(5 * 2) (5 + 1) * (5 + 1) + (5 * 2) * (5 * 2)
인수 우선 평가모델과 같은 결과인 136이라는 답이 나오긴 하지만 그 과정이 다릅니다. 특히 이번에는 5 + 1과 5 * 2 가 각각 두 번씩 평가되었습니다. 표현식 x * x의 축약 과정에서 x가 각각 5 + 1과 5 * 2로 치환되었기 때문입니다.
이처럼 먼저 완전히 전개한 후 축약하는 평가 방법을 정상 순서 평가 또는 표준 순서 평가라고 합니다. 반면 먼저 인수들을 평가한 후 적용하는 평가 방법(인터프리터가 실제사용)은 인수 우선 평가 또는 적용적 순서 평가라고 부릅니다. 치환 모형으로 평가할 수 있으며 적법한 값들을 산출하는 함수 적용의 경우 정상 순서 평가와 인수 우선 평가가 같은 값을 산출함을 증명하는 것이 가능합니다.
자바스크립트는 인수 우선 평가 방식을 사용하는데 앞의 5 + 1과 5 * 2에서 본 것처럼 같은 표현식이 여러 번 평가되어 생기는 비 효율성을 피하는 것도 이유이긴 하지만, 좀 더 중요한 이유는 치환 모델을 벗어난 함수에 대해서는 정상 순서 평가가 훨씬 복잡하다는 점입니다.
1.1.6 조건부 표현식과 술어
지금까지 배운 것만으로 선언할 수 있는 함수는 그 표현력이 제한적입니다. 특히 어떤 조건을 판별한 후 결과에 따라 서로 다른 연산을 수행해야 하는 방법을 아직 모르는데요. 예를 들어 주어진 수의 절댓값을 구하려면 다음에서 보듯이 주아진 값이 음수인지 아닌지에 따라 다른 연산을 수행할 수 있어야 합니다.
사례가 여러 개인 사례 분석 구조를 자바스크립트에선 다음처럼 대안 표현식들이 다른 조건부 표현식 안에 있는 형태로 중첩된 표현식으로 표현할 수 있습니다.
function abs(x) { return x > 0 ? x : x === 0 ? 0 : - x; }
조건부 표현식 구문형은 오른쪽 결합이기 때문에 x === 0? 0 : - x를 괄호로 감싸줄 필요는 없습니다. 인터프리터는 빈칸과 줄 바꿈을 무시하기 때문에 코드 포매팅은 오직 가독성을 위한 것입니다. 사례 분석 구조의 일반적인 형태는 다음과 같습니다.
술어 P1과 해당 귀결 표현식 e1을 묶어서 하나의 절이라고 하며 이런 어법에서 하나의 사례 분석 구조는 일련의 절들 끝에 하나의 최종 표현식이 있는 형태라고 할 수 있습니다. 먼저 술어 P1이 평가되며 만일 그 값이 거짓이면 P2가 평가되고 만일 P2도 거짓이면 P3가 평가됩니다. 이런 과정이 반복되며 참으로 평가되는 술어가 나오면 해당 귀결 표현식 e를 평가하여 그 값을 사례 분석 구조 전체의 값으로 돌려줍니다. 만약 모든 p가 거짓이라면 사례 분석 구조 전체의 값은 최종 대안 표현식의 값입니다.
수들에 적용되는 >= 나 >, <=, ===,!== 같은 원시 술어들 외에, 복합 술어를 구축하는 데 사용할 수 있는 논리 조합 연산도 있습니다.
- 표현식 1 && 표현식 2
이 연산은 논리곱에 해당합니다. and라고도 불립니다. - 표현식 1 || 표현식 2
이 연산은 논리합에 해당합니다 or라고도 불립니다. - ! 표현식
이 연산은 논리 부정에 해당합니다. not이라고도 하며 논리부정 표현식의 값은 표현식이 거짓으로 평가되면 참이며 참으로 평가되면 거짓입니다.
&&와 || 는 연산자가 아닙니다. 이들의 우측에 오는 포현식이 항상 평가되지는 않습니다. 반면! 는 연산자이며 1.1.3의 평가 규칙을 따릅니다. 다른 연산자는 인수를 두 개 받는 이항 연산자이지만 이! 는 인수하나 만 받는 단항 연산자입니다. 연산자 !를 인수 앞에 둔 방식을 전위 연산자라고 하며 -x에 쓰인 수치 부정 연산자 -도 전위 연산자 입니다.
이 술어들의 사용법은 아래와 같습니다. 다음은 수 x가 5 < x < 10 구간에 있는지 판정하는 표현식입니다.
x > 5 && x < 10
구문형 &&는 우선순위가 비교 연산자 >, < 보다 낮습니다. 그리고 조건부 표현식 구문형..?.. :.. 의 우선순위는 지금껏 배운 모든 연산자보다 낮습니다. 앞의 abs 함수에도 이 점이 쓰였습니다. 다른 예를 들면 다음 함수는 첫 인수가 둘째 인수보다 크거나 같은지 판정합니다.
function grater_or_equal(x, y) { return x > y || x === y; }
다음과 같이 표현할 수도 있습니다.
function greater_or_equal(x, y) { return ! (x < y); }
두 수에 적용된 함수 greater_or_equal은 >= 연산자와 동일하게 동작하며 단항 연산자는 이항 연산자보다 우선순위가 높기 때문에 이 예에서 괄호는 꼭 필요합니다.
연습문제 1.2
다음 수식을 자바스크립트 표현식으로 옮겨라
풀이
5 + 4 + (2-(3-(6 + 5/4))) / (3 * (6-2) * (2-7))
연습문제 1.3
세 개의 수를 받고 셋 중 가장 작은 것을 제외한 두 수의 제곱들을 합한 경과를 돌려주는 함수를 선언하라
function square_to_min(x, y, z) { return x < y && z < x ? square(x) + square(y) : y < z && x < y ? square(y) + square(z) : square(x) + square(y); }
연습문제 1.4
앞에서 본 함수 적용 평가 모형은 함수 표현식이 복합 표현식인 경우도 허용한다. 이 점을 고려해서 a_plus_abs_b 함수의 작동 방식을 서술하라.
function plus(a, b) { return a + b; } function minus(a, b) { return a - b; } function a_plus_abs_b(a, b) { return (b >= 0 ? plus : minus)(a, b); }
풀이
a_plus_abs_b(10, 30); 20 >= 10 ? plus(10, 20) : minus(10, 20); 20 >= 10 ? 10 + 20 : 10 - 20; 10 + 20; 30
연습문제 1.5
벤 빗디들은 주어진 인터프리터가 인수 우선 평가를 사용하는 정상 순서를 평가하는지 파악하는 방법을 고안했다. 이를 위해 벤은 다음 두 함수를 선언했다.
function p() { return p(); } function test(x, y) { return x === 0 ? 0 : y; }
이제 다음과 같은 문장을 평가하면 해석기의 평가 방식을 파악할 수 있다.
test(0, p());
인터프리터가 인수 우선 평가를 사용할 때와 정상 순서평가를 사용할 때 이 문장이 어떤 식으로 평가되는지 각각 서술하라 (인터프리터가 정상 순서이든 인수 우선이든 조건부 표현식의 평가 규칙은 동일하다고 가정할 것, 즉 어떤 경우이든 술어 표현식이 제일 먼저 평가되고 그 결과에 따라 귀결 표현식 또는 대안 표현식이 평가된다.)
정상 순서 평가
test(0, p()); x === 0 ? 0 : p(); 0
인수 우선 평가
test(0, p()); x === 0 ? 0 : p(); Maximum Call Stack Error
이므로 자바스크립트는 인수 우선 평가임을 알 수 있다.
이번에는 정상 순서 평가와 인수 우선 평가에 대해서 각각 서술하는 부분이 좀 어려웠는데 인수 평가하여 함수의 인수로 대입하는 경우가 일반적 인터프리터의 형태라는 것을 알게 되었고 자바스크립트에서 이를 테스트해 볼 수 있는 벤의 아이디어가 좋았습니다.
특히 그 5 + 1이 왜 두 번 평가되는지 이해가 안 됐으나 정상 순서 평가라면 모두 대입한 후 평가하기 때문에 그렇다는 사실을 알게 됐습니다.
'Tech' 카테고리의 다른 글
자바스크립트로 배우는 SICP - 함수를 이용한 추상화(1) (0) 2023.05.24 자바스크립트로 배우는 SICP - 들어가며 (0) 2023.05.23 RCA, XLR, TS, TLS? 라인에 대해서 알아보자 (0) 2023.05.18 디자인 패턴, 파사드(Facade pattern) (0) 2023.05.18 디자인 패턴, 싱글톤(Singleton pattern) (0) 2023.05.18 댓글