* Arithmetic Expression (수학적 표현식)
- 수학적 표현식은 다음의 4가지로 구성이 된다.
> Operator(연산자), Operands(피연산자), Parentheses(괄호), Function calls(함수 호출)
- 수학적 표현식을 나타낼때는 다음의 설계 이슈를 가진다.
> 연산자 우선순위는 어떻게 할 것인가?
> 연산자 결합법칙은 어떻게 할 것인가?
> 피연산자들의 평가 순서는 어떻게 할 것인가?
> 부작용에 대한 대처는 어떻게 해줄 것인가?
> 다양한 타입이 섞여있는 표현을 허락할 것인가?
* 연산자 우선순위
- 연산자 종류는 연산자하나에 오는 피연산자들의 수에 따라서 달라진다.
> A unary operator(단항연산자) : -a, -1, ++a
> A binary operator(이항연산자) : a + b, a- b
> A ternary operator(삼항연산자) : ( a - b ) ? 1 : 2
- 일반적인 연산자 우선순위
ⓐ 괄호
ⓑ 단항 연산자
ⓒ ** ( 승을 구하는 연산자, 3**4 = 3^4 // 지원을 해준다면 ⓒ번 자리에 위치한다 )
ⓓ *, /
ⓔ +, -
* 연산자 결합법칙
- 연산자의 결합법칙은 우선순위가 같은 연산에 대해서 어떤 순서를 통해서 계산을 할 것인가에 대한 문제이다.
- 일반적인 경우 : "**" 연산자를 제외하고는 왼쪽에서 오른쪽을 연산을 실시해준다.
(종종 예외적인 경우는 단항연산자도 **과 같이 오른쪽에서 왼쪽으로 실시해준다.)
- 연산자 결합법칙을 파괴한 언어 : APL
> 연산자 우선순위는 존재하지 않으며 오로지 오른쪽에서 왼쪽방향으로 연산을 실시한다.
> 괄호를 통해서 우선순위를 알려줘야한다.
* 피연산자 평가 순서
- 피연산자의 형태에 따라서 달라진다.
1. 변수 : 단지 메모리에서 값을 가져온다.
2. 상수 : 메모리에서 가져오는 경우도 있고, 기계언어에 정의된 값을 가져 올 수도 있다.
3. 괄호 : 모든 피연산자나 연산자들보다 먼저 평가를 받는다.
4. 함수참조 : 함수참조를 피연산자로 사용함에 따라서 부작용이 발생한다.
이 부작용과 부작용을 해결하는 방법에 따라 여러가지로 나뉜다.
* 부작용에 대한 처리
- 함수 참조를 피연산자로 사용함에 따라 발생하는 부작용을 주로 볼 것이다.
- 함수 참조를 피연산자로 사용했을시 어떠한 문제가 발생하는가? 모호함이 발생합니다.
ex) void fun(int a){ return a/2; }
int main(void){ int a = 10; int b = a + fun(&a); printf("%d\n",b); } // b를 출력한다면...
> b의 값은 10 + 5 를 하여 15인가? 아니면 5 + 5 를 하여 10인가?
> 두가지로 나뉘는 이유는 단순하다.
함수 호출을 먼저(오른쪽 먼저)한다면 a를 참조변수로 사용하여 a값이 변한후 더하기를 실시한다.(5)
반대로 왼쪽먼저 실시를 한다면, a 값이 10에서 변하지 않고, 함수 반납값과 더하기를 실시할 것이다. (15)
- 부작용을 처리하는 방법에서 프로그래머가 하는 방법은 다음과 같다.
> 함수로 넘기는 파라메터가 식에 포함 되지 않게 하는 방법이다. 위를 예로 들어 main을 수정해보겠다.
int main(void){ int a = 10; int b; int temp = fun(&a); b = a + temp; }
등과 같이 프로그래머에 의해서 원하는 방향으로 이끌어 가는 법이 있다.
이렇게 하는 방식을 참조투명성이라고한다.
- 컴퓨터 설계시의 방법
ⓐ 함수호출을 피연산자로 사용하지 않는다.
> 이것은 움직이지만, 파라미터의 유연성과 로컬변수의 참조가 안된다.
ⓑ 연산순서를 고정시켜놓는다.
> 몇몇 컴파일러는 제한이 고정되있다.
* Operator Overloading (연산자 오버로딩)
- 연산자 재정의
- 이미 많은 곳에 보편화가 되어있다.
ex) '+' c = a + b; // a와 b의 값이 int형이든, float이든 사용이 가능하게 오버로딩이 되어있다.
- 주소연산자(in C/C++) / bit끼리의 and연산
ex) a = &b; // b의 주소값을 a에 저장한다.
a = a&b; // a와 b의 비트 연산을 하여 a에 저장한다.
> 이 경우는 기능도 다르고, 피연산자수도 다르다. 사용자가 잘못할 시에도 컴파일러는 잡아내지 못한다.
- 나누기 연산
ex) a와 b와 c는 모두 float형이다.
c = (int)a/b; // a를 정수로 케스팅되어서 나눈값의 나머지를 모두버린후, .0을 추가하여 c에 저장
c = a/b; // a/b의 값을 구한다.
> 이러한 경우 사용자 및 판독자의 이해를 돕기위해 새로운 symbol을 도입한다.
> 몫을 구할때는 c = a div b; 와 같은 것을 사용하세요~
- 연산자 오버로딩을 사용할시 사용자가 정의한 오버로딩과 기본 제공된 오버로딩이 혼재
> 판독성에 문제를 제기해준다.
- 몇몇 시스템적으로 치명적인 영향을 미칠 수 있는 연산자는 오버로딩을 컴파일러가 제한한다.
ex) = 연산자
* Type Conversion(타입전환)
- 피연산자들의 타입이 다양한 것(Mixed-mode expression)에 의한 에러를 방지하기 위해서 필요하다.
ex) int a, b; float c,d; a = a * b + c / d;
- Conversion 행동에는 narrowing conversion과 widening conversion이 존재한다.
> narrowing conversion은 "a = c + d;" 와 같이 큰 값을 작은 곳에 넣으려고 한다. 값의 손실이 발생
> widening conversion은 "c = a + b;" 와 같이 작은 값을 큰 곳에 넣으려고 하는 것으로 값 손실 미발생
- Implicit Type Conversion(묵시적 형변환)
> Coercion
> 작성력이 향상되지만, 컴파일수준에서 발견가능한 Type error가 무시되는 경향이 있다.
-> widening conversion의 경우에는 이로인한 문제가 발생하지 않는다.
- Explicit Type Conversion(명백한 형변환)
> 흔히 알고 있는 casting이다.
ex) (In C) (float)a;
(In Ada) float(a); // 형변환도 함수처럼 사용하는 Ada가 유연성이 더좋다.
* 표현상의 오류
- 수학적인 계산 오류 > 0으로 어떠한 수를 나누려고 할 때
- 수학적 알고리즘을 컴퓨터로 연산할때 나타나는 문제(메모리 한계) > overflow, underflow
- run-time system에 의해 error을 발견할 수 있다.
* Relational Expression(관계 표현식)
* Boolean Expression(참/거짓 표현식)
- T/F값 끼리의 계산을 실시
- c 언어의 경우는 boolean type을 사용하지 않는다. C의 경우는 0은 false이고 나머지는 true값이된다.
- c언어에서는 a<b<c의 연산이 가능하다. 이의 사용자 의도는 b가 a와 c사이에 있는 값임을 보이는 것이다.
> 하지만 실제 계산은 b<c의 true와 false를 return하여 a<result 연산을 하는 엉뚱한 계산을 하고 만다.
> 주의하도록하자!!
* 모든 연산자들의 우선순위
- Pascal : not unary- /// * / div mod and /// + - or /// relops(논리연산)
- Ada : ** /// * / mod rem /// unary - not /// + - & /// relops /// and or xor
> Ada의 경우 relops(논리연산)의 우선순위를 할때 "()"를 통해서 나눠줘야한다. 그렇지 않으면 error
- 자바와 c의 경우에는 50개의 연산자와 17개의 다른 연산 단계를 가지고 있다.
- 모든 언어에서 ()의 연산자는 가장 높다.
* Short Circuit Evaluation(지름길 평가)
- 모든 값을 계산하지 않아도 결과값을 알수 있는경우, 계산과정을 스킵한다.
- 효율성을 위해서 개발된 경우가 많다.
> 예를 들어, 0 으로 시작하는 곱 "0*1*2*3*4*..."의 결과값은 0을 본 것만으로도 0임을 알수 있다.
> 논리식의 예를 보면, or에서 맨앞이 true일 경우 뒤에 것을 안해도되고,
반대로 and에서 맨앞이 false면 뒤에 것은 실행하지 않아도 된다.
- Short Circuit Evaluation에 의해서 부작용이 발생하는데 다음과 같은 경우가 해당한다.
> ( a == b ) || ( ++b > 3 ) // 만약 a==b가 true이면 b값은 변화가 없이 실행하겟지만,
// a==b가 false면 ++b도 실행해줘야한다.
> 이는 신뢰성을 떨어 뜨리는 상항을 초래한다.
- 부작용에 대한 각언어의 대처방법
> (In Ada) 사용자가 명시해주도록 한다.
and나 or을 이용하여 관계표현을 하면, Short Circuit Evaluation을 실시 하지 않는다.
and then 이나 or else를 이용하면 Short Circuit Evaluation을 실시한다.
* Assignment Statements(할당의 표현)
- = 와 := 의 심볼중 둘중에 하나를 언어에서 사용을 한다.
- :=를 사용하는 것은 =를 같다 연산을 하기위해서 아끼는 것이다.(판독력 향상을 위해서)
- PL/I는 =로 같다와 Assignment로 둘다 사용한다.
> A = B = C; 이 경우 PL/I의 판단은 어떠할까?
- 이에 의해 치명적인 문제를 제공하는 연산자는 오버로딩을 제한하고 하나의 의미로 사용되는 것이다.
* Mixed-Mode Assignment
- C/C++/FORTRAN의 경우는 narrowing conversion과 widening conversion이 모두 제공된다.
효율을 위해서 탄생했으므로...
- Pascal과 Java의 경우는 widening conversion만 제공을 해준다.
- Ada의 경우는 모두다 제공을 해주지 않는다. 신뢰성을 위해서 탄생했으므로...
- 수학적 표현식은 다음의 4가지로 구성이 된다.
> Operator(연산자), Operands(피연산자), Parentheses(괄호), Function calls(함수 호출)
- 수학적 표현식을 나타낼때는 다음의 설계 이슈를 가진다.
> 연산자 우선순위는 어떻게 할 것인가?
> 연산자 결합법칙은 어떻게 할 것인가?
> 피연산자들의 평가 순서는 어떻게 할 것인가?
> 부작용에 대한 대처는 어떻게 해줄 것인가?
> 다양한 타입이 섞여있는 표현을 허락할 것인가?
* 연산자 우선순위
- 연산자 종류는 연산자하나에 오는 피연산자들의 수에 따라서 달라진다.
> A unary operator(단항연산자) : -a, -1, ++a
> A binary operator(이항연산자) : a + b, a- b
> A ternary operator(삼항연산자) : ( a - b ) ? 1 : 2
- 일반적인 연산자 우선순위
ⓐ 괄호
ⓑ 단항 연산자
ⓒ ** ( 승을 구하는 연산자, 3**4 = 3^4 // 지원을 해준다면 ⓒ번 자리에 위치한다 )
ⓓ *, /
ⓔ +, -
* 연산자 결합법칙
- 연산자의 결합법칙은 우선순위가 같은 연산에 대해서 어떤 순서를 통해서 계산을 할 것인가에 대한 문제이다.
- 일반적인 경우 : "**" 연산자를 제외하고는 왼쪽에서 오른쪽을 연산을 실시해준다.
(종종 예외적인 경우는 단항연산자도 **과 같이 오른쪽에서 왼쪽으로 실시해준다.)
- 연산자 결합법칙을 파괴한 언어 : APL
> 연산자 우선순위는 존재하지 않으며 오로지 오른쪽에서 왼쪽방향으로 연산을 실시한다.
> 괄호를 통해서 우선순위를 알려줘야한다.
* 피연산자 평가 순서
- 피연산자의 형태에 따라서 달라진다.
1. 변수 : 단지 메모리에서 값을 가져온다.
2. 상수 : 메모리에서 가져오는 경우도 있고, 기계언어에 정의된 값을 가져 올 수도 있다.
3. 괄호 : 모든 피연산자나 연산자들보다 먼저 평가를 받는다.
4. 함수참조 : 함수참조를 피연산자로 사용함에 따라서 부작용이 발생한다.
이 부작용과 부작용을 해결하는 방법에 따라 여러가지로 나뉜다.
* 부작용에 대한 처리
- 함수 참조를 피연산자로 사용함에 따라 발생하는 부작용을 주로 볼 것이다.
- 함수 참조를 피연산자로 사용했을시 어떠한 문제가 발생하는가? 모호함이 발생합니다.
ex) void fun(int a){ return a/2; }
int main(void){ int a = 10; int b = a + fun(&a); printf("%d\n",b); } // b를 출력한다면...
> b의 값은 10 + 5 를 하여 15인가? 아니면 5 + 5 를 하여 10인가?
> 두가지로 나뉘는 이유는 단순하다.
함수 호출을 먼저(오른쪽 먼저)한다면 a를 참조변수로 사용하여 a값이 변한후 더하기를 실시한다.(5)
반대로 왼쪽먼저 실시를 한다면, a 값이 10에서 변하지 않고, 함수 반납값과 더하기를 실시할 것이다. (15)
- 부작용을 처리하는 방법에서 프로그래머가 하는 방법은 다음과 같다.
> 함수로 넘기는 파라메터가 식에 포함 되지 않게 하는 방법이다. 위를 예로 들어 main을 수정해보겠다.
int main(void){ int a = 10; int b; int temp = fun(&a); b = a + temp; }
등과 같이 프로그래머에 의해서 원하는 방향으로 이끌어 가는 법이 있다.
이렇게 하는 방식을 참조투명성이라고한다.
- 컴퓨터 설계시의 방법
ⓐ 함수호출을 피연산자로 사용하지 않는다.
> 이것은 움직이지만, 파라미터의 유연성과 로컬변수의 참조가 안된다.
ⓑ 연산순서를 고정시켜놓는다.
> 몇몇 컴파일러는 제한이 고정되있다.
* Operator Overloading (연산자 오버로딩)
- 연산자 재정의
- 이미 많은 곳에 보편화가 되어있다.
ex) '+' c = a + b; // a와 b의 값이 int형이든, float이든 사용이 가능하게 오버로딩이 되어있다.
- 주소연산자(in C/C++) / bit끼리의 and연산
ex) a = &b; // b의 주소값을 a에 저장한다.
a = a&b; // a와 b의 비트 연산을 하여 a에 저장한다.
> 이 경우는 기능도 다르고, 피연산자수도 다르다. 사용자가 잘못할 시에도 컴파일러는 잡아내지 못한다.
- 나누기 연산
ex) a와 b와 c는 모두 float형이다.
c = (int)a/b; // a를 정수로 케스팅되어서 나눈값의 나머지를 모두버린후, .0을 추가하여 c에 저장
c = a/b; // a/b의 값을 구한다.
> 이러한 경우 사용자 및 판독자의 이해를 돕기위해 새로운 symbol을 도입한다.
> 몫을 구할때는 c = a div b; 와 같은 것을 사용하세요~
- 연산자 오버로딩을 사용할시 사용자가 정의한 오버로딩과 기본 제공된 오버로딩이 혼재
> 판독성에 문제를 제기해준다.
- 몇몇 시스템적으로 치명적인 영향을 미칠 수 있는 연산자는 오버로딩을 컴파일러가 제한한다.
ex) = 연산자
* Type Conversion(타입전환)
- 피연산자들의 타입이 다양한 것(Mixed-mode expression)에 의한 에러를 방지하기 위해서 필요하다.
ex) int a, b; float c,d; a = a * b + c / d;
- Conversion 행동에는 narrowing conversion과 widening conversion이 존재한다.
> narrowing conversion은 "a = c + d;" 와 같이 큰 값을 작은 곳에 넣으려고 한다. 값의 손실이 발생
> widening conversion은 "c = a + b;" 와 같이 작은 값을 큰 곳에 넣으려고 하는 것으로 값 손실 미발생
- Implicit Type Conversion(묵시적 형변환)
> Coercion
> 작성력이 향상되지만, 컴파일수준에서 발견가능한 Type error가 무시되는 경향이 있다.
-> widening conversion의 경우에는 이로인한 문제가 발생하지 않는다.
- Explicit Type Conversion(명백한 형변환)
> 흔히 알고 있는 casting이다.
ex) (In C) (float)a;
(In Ada) float(a); // 형변환도 함수처럼 사용하는 Ada가 유연성이 더좋다.
* 표현상의 오류
- 수학적인 계산 오류 > 0으로 어떠한 수를 나누려고 할 때
- 수학적 알고리즘을 컴퓨터로 연산할때 나타나는 문제(메모리 한계) > overflow, underflow
- run-time system에 의해 error을 발견할 수 있다.
* Relational Expression(관계 표현식)
* Boolean Expression(참/거짓 표현식)
- T/F값 끼리의 계산을 실시
- c 언어의 경우는 boolean type을 사용하지 않는다. C의 경우는 0은 false이고 나머지는 true값이된다.
- c언어에서는 a<b<c의 연산이 가능하다. 이의 사용자 의도는 b가 a와 c사이에 있는 값임을 보이는 것이다.
> 하지만 실제 계산은 b<c의 true와 false를 return하여 a<result 연산을 하는 엉뚱한 계산을 하고 만다.
> 주의하도록하자!!
* 모든 연산자들의 우선순위
- Pascal : not unary- /// * / div mod and /// + - or /// relops(논리연산)
- Ada : ** /// * / mod rem /// unary - not /// + - & /// relops /// and or xor
> Ada의 경우 relops(논리연산)의 우선순위를 할때 "()"를 통해서 나눠줘야한다. 그렇지 않으면 error
- 자바와 c의 경우에는 50개의 연산자와 17개의 다른 연산 단계를 가지고 있다.
- 모든 언어에서 ()의 연산자는 가장 높다.
* Short Circuit Evaluation(지름길 평가)
- 모든 값을 계산하지 않아도 결과값을 알수 있는경우, 계산과정을 스킵한다.
- 효율성을 위해서 개발된 경우가 많다.
> 예를 들어, 0 으로 시작하는 곱 "0*1*2*3*4*..."의 결과값은 0을 본 것만으로도 0임을 알수 있다.
> 논리식의 예를 보면, or에서 맨앞이 true일 경우 뒤에 것을 안해도되고,
반대로 and에서 맨앞이 false면 뒤에 것은 실행하지 않아도 된다.
- Short Circuit Evaluation에 의해서 부작용이 발생하는데 다음과 같은 경우가 해당한다.
> ( a == b ) || ( ++b > 3 ) // 만약 a==b가 true이면 b값은 변화가 없이 실행하겟지만,
// a==b가 false면 ++b도 실행해줘야한다.
> 이는 신뢰성을 떨어 뜨리는 상항을 초래한다.
- 부작용에 대한 각언어의 대처방법
> (In Ada) 사용자가 명시해주도록 한다.
and나 or을 이용하여 관계표현을 하면, Short Circuit Evaluation을 실시 하지 않는다.
and then 이나 or else를 이용하면 Short Circuit Evaluation을 실시한다.
* Assignment Statements(할당의 표현)
- = 와 := 의 심볼중 둘중에 하나를 언어에서 사용을 한다.
- :=를 사용하는 것은 =를 같다 연산을 하기위해서 아끼는 것이다.(판독력 향상을 위해서)
- PL/I는 =로 같다와 Assignment로 둘다 사용한다.
> A = B = C; 이 경우 PL/I의 판단은 어떠할까?
- 이에 의해 치명적인 문제를 제공하는 연산자는 오버로딩을 제한하고 하나의 의미로 사용되는 것이다.
* Mixed-Mode Assignment
- C/C++/FORTRAN의 경우는 narrowing conversion과 widening conversion이 모두 제공된다.
효율을 위해서 탄생했으므로...
- Pascal과 Java의 경우는 widening conversion만 제공을 해준다.
- Ada의 경우는 모두다 제공을 해주지 않는다. 신뢰성을 위해서 탄생했으므로...