프로그래밍[Univ]/프로그래밍 언어론

[프로그래밍 언어론] Expressions and Assignment Statements

Cloud Travel 2011. 11. 1. 21:49
* 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의 경우는 모두다 제공을 해주지 않는다. 신뢰성을 위해서 탄생했으므로...