[js] 자바스크립트의 식과 문 2 / 3

식과 관련된 두번째 포스팅에서는 식처리기와 연산자를 중심으로 생각해보겠습니다.

저번에 이어 이번에도 도중에 정신을 잃기 쉬운 복잡한 내용이 많이 등장합니다 ^^

 
 
 

연산자의 문제

연산자하면 가장 먼저 떠오르는 것은 산술연산자 일 것입니다. + – * / 로 대표되는 사칙연산자는 수학적인 표현을 흉내내도록 설계되어있습니다. 설계 당시 개발자가 수학적인 교육을 받았다고 생각하여, 수식을 수학적 표현으로 기술하는게 편리할거라 생각했기 때문입니다. 하지만 연산자는 사실 상 함수에 가깝고 일부러 수학적인 해석이 되도록 추가적인 파싱구조를 넣어야합니다. 이 불일치로 수식은 매우 오류가 나기 쉽고 알고리즘 버그의 핵심 원인이 됩니다.

예를 들어 1 + 2 * 3 은 1 + ( 2 * 3 ) 으로 해석되는데, 연산자 우선순위라는 암묵적인 규칙이 있기 때문입니다. 이걸 당연하다고 볼 수 있겠지만 그 당연함이 무엇인지 생각해보면…

연산자 우선 순위의 근거는 프로그래밍적인 로직이 아니라 수학이 그래서 흉내냈다 입니다. 만약 프로그래밍적인 로직이라면 왼쪽에서 오른쪽 해석되는 파싱 규칙이 일관성 있게 적용되어야 할 것입니다.
( 1 + 2 ) * 3

기본적인 문제 외에도 개별 연산자별로 매우 주의해야하는 심각한 문제가 많이 있습니다. 이는 곧 모든 연산자를 아우르는 심각한 문제가 되고, 문과도 연결된 프로그래밍 전략으로 더욱 확장됩니다.

최대한 쉽게 차근차근 진행해보려 합니다만…과연!!
 
 

할당연산자

수학적 표현을 무리하게 흉내내려는 시도는 할당식에서 더욱 문제를 일으킵니다.
자바스크립트의 모든 언어 요소가 왼쪽에서 오른쪽 순으로 처리되는데, 수학의 할당기호가 우변을 좌변으로 할당하는 것을 흉내내기 위해 유일하게 오른쪽에서 왼쪽으로 해석되는 형식이 탄생합니다.
또한 동일함을 비교하는 경우도 수학은 = 을 사용하는데 비해 프로그래밍에서는 할당식과 구분할 필요가 있으므로 == 라는 수학에 없는 기호를 사용하게 되고 이 때문에 초보 개발자들이 수학적으로 로직을 짜다가 에러를 일으킵니다.

이는 모두 원천적으로 수학과 동일할 수 없는 프로그래밍을 억지로 수학 컨텍스트에 적용하려고 시도하여 할당과 동등비교에서 일관성이 깨지고 특이점이 생겨버리기 때문입니다.

이처럼 할당연산은 결국 전체 언어 상에서 식의 유일한 특이점이 되어버리기 때문에 자바나 c에서는 할당이 분명 수학적인 식임에도 불구하고 할당문(assignment)으로 문의 하나로 별도로 규정합니다.

하지만 자바스크립트는 할당을 연산자로 처리해 식으로 처리됩니다. 따라서 기존의 c나 자바와 비교하여 다음과 같은 코드 상의 차이점이 생깁니다. if(식)문; 의 형식으로 생각해보죠.

//java, c의 경우
if( a = 3 )...; // if(문)문 이 되어 에러!

//js의 경우
if( a = 3 )...; // if(식)문 으로 OK!

자바스크립트에서 = 은 연산자 이므로 결과가 값이 됩니다. 값은 우변을 좌변에 할당한 후 그 좌변의 값입니다.

우변을 좌변을 할당했으니 당연히 좌우변 중 아무 값이나 마찬가지일거라 생각하면 안됩니다. 반드시 좌변에 할당된 이후의 값이란 점을 주의해야합니다. 예를 들어 DOM의 id는 반드시 문자열이 할당됩니다. 그럼 다음과 같은 경우 무엇이 최종결과가 될 것인가 생각해보죠.

var dom = document.getElementById('test');
var id = dom.id = 108;
alert( typeof id ); //'string'

위의 코드에서 108 즉 숫자를 dom.id에 할당했지만 자동형변환이 일어나 dom.id의 값은 문자열 ‘108’이 됩니다.
따라서 id에 들어있는 것도 숫자가 아닌 문자열이 됩니다. 코드로 표현하면 아래와 같습니다.

dom.id = 108; //dom.id=='108'
var id = dom.id; //'108'

이 부분은 약간만 주의하면 괜찮지만 할당이 우에서 좌로 해석되는 특이점은 큰 문제를 일으킵니다. 다음의 코드를 보죠.

a = b, c;

사실 상 위의 처리는 (a = b), c 로도 가능하고, a = (b, c) 로도 해석할 수 있습니다. 할당식이 무엇에 우선하는 가의 문제입니다. 할당연산자는 우선순위가 낮기 때문에 a = (b, c) 로 해석됩니다만, 보통의 개발자 입장에서 이를 이해하는 것은 쉽지 않습니다.

왜냐면 이해할 것이 아니라 누군가 정한걸 그대로 외워야하는 상황이기 때문입니다. 따라서 할당연산자를 사용하는 경우 개발자의 인지적인 문제를 해결하기 위해 괄호를 남발하게 됩니다.

var a;
var b = 50;
var c = 100;

alert( ( a = b ), c ); //a==50, 출력 100
alert( a = ( b, c ) ); //a==100, 출력 100

 
 

연산자 분류

슬슬 연산자의 정체를 생각해보겠습니다. 연산식이 하나의 문자열이라면 결국 식처리 토크나이저로 파싱트리를 만든 뒤 노드별로 처리되는데, 이때 각 연산자는 일종의 함수로 볼 수 있습니다.

1 + 2 * 3 의 경우 식 구문분석이 끝난 결과는 아래와 같을 것입니다.

multi( plus( 1, 2 ), 3 )

결국 연산식이란

  1. 식처리기를 통해 수학적인 우선 순위(일명 연산자 우선순위)를 판단한 뒤,
  2. 각 연산자를 처리하는 함수를 부르는 번역과정

으로 볼 수 있습니다. 앞 서 설명한대로 이 우선 순위는 프로그램과 무관하게 수학적인 의미로 정해진거라 개발자가 외우고 있는 수학적 지식에 근거를 두고 있습니다. 수학을 잘못하면 개발이 힘들어지는 순간 입니다.

근데 수학을 잘해도 문제는 끝나지 않습니다.

프로그래밍에서 식이 포함하고 있는 연산자는 산술연산자로 끝나지 않습니다. 논리연산자를 비롯하여 비트연산자, 할당연산자, 컴마연산자, 조건연산자를 포함하고 있습니다. 이들이 모두 결합한 우선 순위는 인간이 감당할 수 없게 되어버립니다.

var a = 3 ^ 5 / 7 % b = 3, 6;

이런걸 해석하는걸 자랑스럽게 여기는거 자체가 웃길 뿐더러, 버그를 양산하는 코드로 수 많은 그루들이 금지시키고 있습니다.
연산자 우선 순위란 실제 사칙연산을 벗어나는 범위에서 사용하지 않는게 정상적입니다만 이를 위해서는 강제 우선 연산자(괄호!)라는 것을 사용하게 됩니다.

연산자란 이처럼 다양한데 어떻게 연산자를 분류해야하는 걸까요?

우선 연산자가 함수라는 관점이 있습니다. 이 관점에서 연산자 분류는 받는 인자의 갯수로 정합니다. 연산자가 받는 인자는 보통 ‘항’이라고 표현하는데 이것도 수학에서 빌려온 오묘한 단어입니다(수학에서의 항과 연산자가 의미하는 항은 상당한 개념차이가 있습니다)
쨌든 항을 두 개 받는 연산자를 생각해보죠.

3 + 2
3 * 2

위에서 연산자를 함수라고 생각하면 두 개의 인자를 받는 함수로 정리할 수 있고 아래와 같이 익숙한 함수호출구문으로 바꿀 수 있을 것입니다.

+( 3, 2 )
*( 3, 2 )

함수이름이 기호라서 적응이 안된다구요? 그럼 진짜로 짜봅시다.

var calc = {
'+':function( a, b ){return a + b;},
'*':function( a, b ){return a * b;}
};

calc['+']( 3, 2 )
calc['*']( 3, 2 )

대략 이 정도일 것입니다. 이렇듯 두 개의 인자를 받는 연산자를 2항 연산자라 합니다. 이런 분류 기준이라면 0항 연산자, 1항 연산자, 2항 연산자, 3항 연산자…등 무한한 조합을 생각해볼 수 있습니다만 자바스크립트가 지원하는 연산자는 3항 연산자까지 입니다.

또 다른 분류 기준은 연산자의 용도에 따른 것입니다. 이 분류에 따르면 산술, 논리, 비트, 할당 연산자 등 4가지 기본 분류와 문자열연산자와 특수연산자 9가지가 존재합니다.

연산자를 분류할 때 많은 책이나 개발자들이 이 두 가지 기준을 혼용하여 사용하기 때문에 애매함에 휩싸이게 됩니다(사실 함수적인 관점에서 모든 연산자는 함수로서 유일한 기능을 지니고 있으니 차라리 항의 수로 구분하는게 편리할 때도 있습니다 ^^)
 
 

무항 연산자와 단항 연산자

항이 없는 연산자가 유일하게 하나 있는데 this 입니다. this 는 함수의 시스템에서 등장하는 녀석이지만 일단 언어 상으로 볼 때 연산자로 분류되어있습니다. 뭐 일단 값입니다! 그 점에서 상수나 객체라고 하기엔 매번 가리키는 대상이 달라지고 값이라고 하기엔 지정할 수 없으니 식시스템에서 분류할 수 이는 유일한 방법은 연산자 입니다. 그런면에서 타당하죠.
단항 연산자는 엄청나게 많습니다. typeof 는 뒤에 오는 항의 타입을 문자열로 토합니다. !는 논리적인 역전을 시키죠.

하지만 이 외에도 중요한 단항 연산자로는 +, – 가 있습니다 이 기호는 2항 연산자로도 작동하기 때문에 헛갈리기 쉬운데, 단항 연산자일 때는 전혀 다른 의미로 작동하기 때문에 주의해야 합니다.

var a = +new Date;

위와 같은 구문에서 +는 1항 연산자로 쓰이는데 의미는 숫자화 시킨다 입니다. 따라서 아래의 코드와 같은 효력이 일어납니다.

var a = (new Date).valueOf();

이 외에도 new, delete, void 등이 있습니다만, 전부 설명하면 길어지므로 호기심이 생기셨다면 하나하나 반드시 탐구해보시길 권장합니다. 하나같이 매우 중요합니다.
 
 

괄호 연산자

위의 단항 연산자와 전혀 다른 매우 이질적인 단항 연산자가 있는데 바로 괄호입니다. 괄호는 강제 연산 우선 연산자입니다. 근데 다른 단항 연산자가 항의 앞에 기술된 것에 비해 항을 감싸는 형태로 생겼습니다. 이 형태로는 유일한 연산자 입니다.
이 괄호 연산자는 매우 중요하면서도 가장 무거운 연산자 입니다. 아래의 식을 생각해보죠.

var a = 1 + ( 3 + 4 + ( 5 + 6 ) );

괄호가 두 번 쓰였고 특히 중첩되어 쓰였습니다. 이를 해소하기 위해서는

  1. 우선 5 + 6 이 해결된 뒤,
  2. 이를 이용해 3 + 4 + 11 을 해결하고,
  3. 마지막으로 전체 연산식을 해결합니다

이거 뭔가 함수의 스택관리와 엄청 닮아있지 않습니까? 괄호식이 일단 등장하면 내부의 모든 괄호가 순차적으로 해결되기 전까지는 괄호식은 절대로 끝나지 않습니다. 그야말로 함수 내에서의 다른 함수를 호출했을 때와 같은 스택시스템과 꼭 닮아있습니다.

식처리기도 결국 내부적으로는 함수시스템과 별다를바없기 때문에 함수의 중첩된 호출이 스택오버플로우를 일으키는 것처럼 괄호의 중첩도 스택오버플로우를 일으킵니다.

이는 브라우저가 식처리기를 구현한 방식에 크게 다른데 크롬 같은 경우는 보통 1200단계 정도의 괄호 중첩에서 뻗어버리는데 비해 IE는 만번이 넘는 중첩에도 끄떡없습니다(식처리기의 스택구현 방법은 이 포스트의 범위를 벗어나니…나중에 다른 포스트에서 다루기로 하죠 ^^;;)

암튼 요점은 식처리기에서 괄호란 공짜가 아니란 점입니다. 스택메모리를 확보해야하는 비싼 연산자 입니다.
 
 

3항 연산자

과감하게 2항 연산자를 건너뛰었습니다. 좋은 책과 자료들이 넘칩니다. 제가 다 적을 필요를 느끼진 않습니다. 게다가 2항 연산자는 워낙 개발자들이 익숙하기도 합니다(그게 또 함정입니다만..=.=)

3항 연산자로 지원되는 유일한 녀석은 조건연산자 입니다. 1 ? 2 : 3 을 통해 3개의 항을 받습니다. 이 연산자를 보고 있자면 언어의 설계자가 사실 같은 기능을 문으로 만들 수도 있고 식으로 만들 수도 있으며 양쪽으로 다 만들 수도 있다는 사실을 알 수 있습니다. 만약 자바스크립트가 루비처럼 모든 문을 식으로 환원한다면 어떤 문이라도 식일 수 있습니다.
식은 값으로만 환원되면 되기 때문에 예를 들어 if문이 참이면 true를 거짓이면 false를 반환하면서 원래 하던대로 움직인다고 생각해봅시다.
그럼 다음과 같이 if문도 식이 되어 값으로 환원될 것입니다.

var a = if( 3 ) alert( 1 ); //if문이 참이므로 a = true;

현대의 언어의 흐름은 점진적으로 문을 식으로 바꿔가는 추세에 있습니다. 아마 이러한 흐름의 시조가 될 법한 제어문형 언어의 연산자가 바로 조건분기연산자인 셈입니다.

이 연산자는 if문 대신 사용되는 경우도 많기 때문에 반드시 그 차이를 비교해서 알아두는 편이 좋습니다. 일단 if else 문을 보면 다음과 같은 형식으로 되어있습니다.

if( 식 ) 문1; else 문2;

이에 비해 조건분기연산자는 다음과 같습니다.

식 ? 식1 : 식2;

즉 if문은 식의 참,거짓에 따라 문을 실행하는데 비해 조건분기연산자는 식을 실행하게 됩니다. 이를 이용하면 문을 식으로 식을 문으로 바꾸는게 가능합니다.

//문
if( a > 3 ) alert( 'ok' );
else alert( 'no' );

//식
a > 3 ? alert( 'ok' ) : alert( 'no' );

위 코드에서 문을 보면 if(식) 식문; else 식문; 으로 되어있습니다(alert호출, 즉 함수의 호출은 값이 됨 == undefined)
이에 비해 아래 식은 전체적으로는 식문; 으로서 하나의 문입니다. 3; 가 아무 문제 없는 식문인 것처럼 조건분기연산자를 포함한 식도 하나의 식이 되어 식문이 됩니다.

단지 여기에는 중대한 차이가 존재합니다.
식문이란 결국 식이 있고 그걸 문에 넣은 것입니다. 즉 문처리기와 식처리가 모두 동원됩니다.
if문의 경우

  1. if문의 제어문 처리
  2. 괄호 안의 식처리
  3. 참일 때 식처리
  4. 식을 문으로 감싸는 식문처리
  5. 거짓일 때 식처리
  6. 식을 문으로 감싸는 식문처리

를 해야합니다.

하지만 조건분기연산식의 경우는

  1. 식처리
  2. 전체식을 감싸는 식문처리

이게 끝입니다.

어떠한 로직을 구현함에 있에 식처리기로 가능하다면 식처리기로 전부 처리하는 편이 훨씬 유리한 거죠. 문처리기와 식처리기를 오갈 수록 언어 하부에서 파싱하거나 실행하는 비용은 더욱 늘어납니다.

따라서 식으로 로직을 정리할지, 문으로 로직을 정리할 지 염두해두면 보다 효율적인 알고리즘을 작성할 수 있습니다.
 
 

다항 연산자

항의 수가 한정되지 않은 무한항을 처리하는 연산자가 바로 컴마연산자입니다.
a, b, c 의 결과는 c가 되는데 사실 컴마연산자 사이의 상태 변화가 없다면 큰 의미는 없습니다.
반대로 할당연산자와 함께 사용하면 상태의 변화를 단계적으로 일으킨 후 최종 값을 넘겨줄 수 있습니다.
우선 간단한 예로 어떤 인자를 받으면 배열로 만들어 반환하는 경우를 생각해봅니다.

function add(){
	var result;
	return result = [], result.concat( arguments ), result;
}

alert( add( 1, 2, 3 ).join() ); //'1,2,3'

컴마로 연결된 최종값이 result이므로 배열이 반환됩니다만, 1새 배열도 할당하고, 2메서드도 호출한 뒤, 3그 결과물을 반환하게 되는 것입니다. 이는 물론 아래와 같이 친숙한 문으로 환원할 수 있습니다.

function add(){
	var result;
	result = [];
	result.concat( arguments );
	return result;
}

하지만 위의 컴마식이 var문, return문 이라는 두 개의 문으로 해결된 것에 비해 아래는 var문, 식문, 식문, return문의 4단계로 처리고 각 식문에서는 식처리 후 문으로 감싸는 2단계 작업이 포함됩니다.

제어문에서 가장 중요한 역할은 반복과 조건분기인데 반복을 처리하는 연산자는 없지만 조건을 분기하는 연산자가 있으므로 컴마연산자와 결합하여 여러 개의 식을 처리하면 조건분기문을 식으로 대체할 수 있게 됩니다.

//문
if( a > 3 ){
	b = 5;
	c = 4;
	arr.push( b );
}else{
	b = 1;
	error();
}

//식
a > 3 ? b = 5, c = 4, arr.push( b ) : b = 1, error() ;

식은 암호같아 알아보기 힘들다면 붙여쓴 문도 매일반입니다. 식도 정렬하여 표현하면 됩니다.

a > 3 ?
	b = 5,
	c = 4,
	arr.push( b )
:
	b = 1,
	error()
;

식이냐 문이냐 그것은 단순히 현재 담당 개발자가 무엇에 익숙하냐의 문제가 아닙니다. 실제 가독성이란 건….뭐랄까 지식과 큰 상관이 있습니다. 자바스크립트밖에 모르는 개발자에게 아무리 가독성 좋은 포인터가 난무하는 c코드를 보여줘봐야 읽지 못합니다.
마찬가지로 식으로 기술된 코드가 가독성이 떨어진다고 생각하는 가장 큰 이유는 식에 대한 이해가 부족하기 때문일 수도 있습니다.

 
 
 

연산지연

if문이나 for문 등이 식의 조건에 따라 내부의 문을 실행하거나 실행하지 않게 되는데, 그렇지 않고 일반적인 문은 쭉 순차적으로 즉시 실행됩니다. 마찬가지로 연산식도 일반적으로는 연산자 우선 순위 및 좌에서 우로 순차적으로 쭉 실행됩니다만, 마치 if문처럼 조건에 따라 분기되거나 실행을 중단시킬 수 있습니다.
이러한 현상을 즉시 연산을 실행하지 않고 조건을 분기할 때까지 미뤄둔다 하여 연산지연이라 합니다.

가장 쉽게 이해할 수 있는 비슷한 연산자는 괄호연산자입니다. 1 * ( 2 + 3 ) 에서 원래 좌에서 우로 실행되어야하는 수식이 우선 정지되어 괄호 안의 계산이 끝날 때까지 실행이 지연됩니다. 하지만 괄호는 앞 서 설명한대로 연산지연시스템이 아니라 별도의 연산을 위한 스택을 확보하는 시스템입니다.

연산지연을 일으키는 것은 논리연산자입니다. 이는 언어마다 구현이 다른데 자바스크립트는 보다 효율적으로 처리되기 위해 연산지연을 일으키도록 설계되어있습니다. && 와 || 가 대표적으로 이에 해당됩니다.

a && b 의 경우 a가 참이고 b도 참이어야 하는데 이를 반대로 생각해보면 a가 거짓이면 이미 b는 판단해볼 필요도 없습니다.
따라서 반환값은 false가 됩니다.
하지만 둘다 참인 경우를 순서대로 생각해보면 a도 참인걸 확인하고 이어서 b가 참인걸 확인하기 때문에 결과값은 true가 아니라 b가 반환됩니다.
즉 아래와 같습니다.

var a = 0 && 'aaa'; //0
var b = 1 && 'aaa'; //'aaa'

둘다 참인 경우지만 true가 반환되는 것이 아니라 판단에 사용되었던 ‘aaa’가 그대로 반환됩니다.

a || b 은 하나라도 참이면 되는 조건인데, 이를 반대로 생각해보면 a가 거짓일 때만 b를 확인할 필요가 있다는 것입니다.

위의 연산지연은 if 문과 거의 동작이 비슷하므로 서로 바꿔쓰기가 가능합니다.

var a = 1;

//문
if( a ) a = 'ok';

//연산지연
a && ( a = 'ok' );

긍정식 평가에 && 를 이용하는데 비해 부정식 평가에 || 를 쓰면 편리합니다.

var a = 0;

//문
if( !a ) a = 'ok';

//식
a || a = 'ok';

이런 연산지연은 조건분기연산자로 대체할 수도 있습니다.

var a = 0;

a ? a = 'ok' : null;

거짓일 때의 처리가 없으므로 그냥 null 을 넣어주면 그만입니다. 언틋보면 && 와 || 이 짧고 더 좋아보입니다.
하지만 연산지연은 조건분기연산자보다 느리고 심지어 if 문보다도 느립니다. 조건분기연산자나 if문은 처음부터 분기된 뒤에 실행할 식 또는 문의 적재공간을 확보하도록 구현되어있는데 비해 &&나 ||은 차례대로 진행되며 실행되거나 멈추는 처리를 하기 때문에 효율적이지 않게 됩니다.
따라서 단순한 할당에는 쓸만합니다만 본격적인 조건분기에는 사용하지 않는 편이 유리합니다.
주로 사용되는 곳은 함수의 기본값 할당 입니다.

function command(){
	var order;
	order = arguments[0] || 'action1';
	...
}

command(); //order == action1

위의 예에서 인자를 받은 경우는 order에 할당하지만 없는 경우는 action1이 됩니다. 하지만 0, ”, null 등으로 넘어와도 action1이 되므로 취약한 구조입니다. 제이쿼리 등 수 많은 라이브러리에서 자주 보이는 코드지만 실제 안정성에는 문제가 많아지기 때문에 적어도 조건분기연산자 정도가 적당하겠죠.

function command(){
	var order;
	order = arguments[0] === 'undefined' ? 'action1' : arguments[0];
}

이렇듯 연산지연시스템의 다양한 면을 둘러봤습니다만 정작 무엇이 스택시스템과 다른지 설명하지 않았습니다.
연산지연은 한단계씩 진행하면서 다음 단계로 넘어가거나 혹은 그 단계의 값을 반환하기 때문에 스택공간이 필요없습니다. 따라서 단계별로 매번 식을 진행할지 말지를 판단하는 요소가 개입되어 느리지만 스택오버플로우의 위험성이 없습니다.

a || b && c ||…..

이런걸 아무리 반복해도 스택오버플로우가 발생하지 않는다는 거죠…이론상으로는!(하지만 구현마다 차이가 있어 엄청 보내면 죽는 브라우저들이 많습니다…)

 
 
 

식의 해석순서

마지막으로 매우 간략하게 살펴볼 내용은 식의 해석입니다. 매우 간단한 원리입니다만 이게 맨날 헛갈립니다. 그래서 간단히 2개의 규칙으로 정리했습니다.

  1. 좌에서 우로 진행
  2. 연산자우선순위 적용

연산자우선순위는 알아서 적용하시는걸로 하고, 여기에서는 좌에서 우로 진행되는 걸 훈련해봅니다.

var a = 0;
var arr = [];
arr[a++] = b(a++) + c(a++);

위 코드에서 각 a++대신 숫자를 쓴다면?

arr[0] = b(1) + c(2);

이유는 좌에서 우로 실행되기 때문에! 할당식은 1좌를 해석하고 2우를 해석한 뒤 3우변을 좌변에 할당합니다. 한 가지 더 보죠.

var a = 1, b = 0, c = 3
alert( a && b || c ); //3

논리연산자 역시 좌에서 우로 읽으면 됩니다. 1 && 0 || 3 에서 우선 1이 참이므로 &&에 의해 b를 해석하는데 b는 0으로 거짓이기 때문에 ||에 의해 c를 해석하여 최종 3이 됩니다.

 
 
 

결론

우선 식과 연산자의 기초를 알아봤습니다.

  1. 식과 문을 혼용하는 것보다 식으로 정리하는 편이 유리하다
  2. 식도 스택을 사용하므로 보다 최적할 수 있는 전략이 필요하다
  3. 컴마연산자와 조건분기연산자의 결합은 많은 문을 대체할 수 있으며 더 효율적이다
  4. 연산지연은 ….느리다!

사실 식처리기를 직접 만들어보면 한 방에 이해할 수 밖에 없습니다.

식처리기를 만들어봅시다!…농담입니다 ^^; 아닌가..

%d 블로거가 이것을 좋아합니다: