Chapter7 - Expressions and Assignment Statements

산술식

  • 프로그래밍 언어에서의 산술식 구성 요소
    1. 연산자
    2. 피연산자
    3. 괄호
    4. 함수 호출
  • 연산자의 종류
    1. 단항
    2. 이항
    3. 삼항 - 세 개의 피연산자를 가짐
  • 이항 연산자의 표기법
    • 대부분의 명령형 프로그래밍 언어에서 중위 표기법
    • perl 과 같은 언어에서는 전위 표기법을 사용
      1. 연산자 우선순위와 결합규칙에 따라 평가(계산) 순서가 결정됨
    • 우선순위
      연산자 우선순위 규칙은 서로 다른 우선순위 수준을 갖는 연산자들이 평가되는 순서를 부분적으로 정의함
    • 덧셈과 뺄셈의 단항 버전
      많은 언어들에서 지원
      단항 덧셈을 항등 연산자라 부름
      → 어떠한 영향도 주지 않는 연산자이기 때문 단항 뺄셈은 그 연산자의 부호를 변경하는 연산자(단, 괄호 사용이 필요)
    • 다음 식들에서의 연산자 우선순위

        - x / y // 
        - x * y //
        - x ** y // 지수 계산
      

      위의 식에서 단항 뺄셈과 이항 연산자간의 상대적 우선순위가 중요하지 않음
      3번째 에서는 단항 뺄셈과 이항 연산자(**)간의 상대적 우선 순위가 중요함
      → 언어들마다 산술 연산자의 우선순위를 정의하고 있음

  1. 결합규칙
    • 덧셈과 뺄셈 연산자가 동일한 우선순위를 갖는다면, 우선순위 규칙은 위의 식의 평가 순서에 대하여 아무것도 말해주지 않은 것임
    • 식이 동일한 수준의 우선순위를 가지면서, 인접된 두 개의 연사자를 포함할 때, 어느 연산자가 먼저 평가될 것인가에 대한 규칙이 결합 규칙임
    • 공통된 언어들의 결합 규칙
      • 왼쪽 부터 오른쪽 순서
      • 예외 : 지수 연산자 (오른쪽 에서 왼쪽 순서) → C, JAVA는 제공안함
  2. 괄호
    • 프로그래머가 식에 괄호를 포함시켜 우선순위 규칙과 결합규칙을 변경할 수 있음
  3. 조건식
    • if-then-else 문이 조건식 배정문을 수행하는데 사용될 수 있음
    • C와 JAVA에서는 삼항연산자로 동일한 조건식 표현 가능 (A ? B : C)
    • python의 경우 (A if B else C)로 표현 가능

피연산자 평가 순서

  • 어떤 피연산자도 부작용을 갖지 않으면, 피연산자의 평가 순서는 무관함
  • 따라서, 피연산자의 평가가 부작용을 가지는 경우에 대한 고려가 필요함
    1. 부작용
    • 함수의 부작용 (함수적 부작용)
      함수가 자신의 매개변수들 중의 하나의 값이나 전역 변수의 값을 변경할 때 발생함
    • 예제 식 : a + fun(a)
      fun() 이 a를 변경하는 부작용을 갖지 않으면, 두 피연산자 a와 fun(a)의 평가의 순서는 식의 값에 영향을 미치지 않음
    • 그러나, fun()이 매개변수 a의 값을 20으로 변경하고 10을 반환하는 경우에, 다음 문장들이 수행되면
      a = 10;
      b = a + fun(a);
      → a의 값이 먼저 인출되면 b = 20
      → fun(a)가 먼저 평가되면, b = 30
    • C 언어 프로그램 예 (전역 변수 변경)

        int a = 5;
        int fun1()
        {
            a = 17;
            return 3;
        }
        void main()
        {
            a = a + fun1();
        }
        /*
        - main()에서 계산된 a의 값은 피연산자들의 평가 순서에 의존적
        - a가 먼저 평가되면, a의 값은 8
        - fun1()이 먼저 평가되면, a의 값은 20
        */
      
    • 피연산자 평가 순서와 부작용의 문제에 대한 두 가지 해결책
      • 함수적 부작용을 허용하지 않은 방법
      • 엄격한 피연산자들의 평가 순서 정의 방법
    • 함수적 부작용을 허용하지 않는 방법
      • 함수형 프로그래밍 언어에서는 함수적 부작용이 없음
      • 명령형 프로그래밍 언어에서는 함수적 부작용이 존재하고 이를 없애는 것은 프로그래머의 유연성을 없애는 요인이됨
      • 함수의 결과로 여러 개의 값을 반환 하는 경우, 구조체를 사용하는 것 외에는 방법이 없어짐
      • Go 언어는 함수가 여러개의 값을 반환하는 것을 지원함
    • 엄격한 피연산자들의 평가 순서 정의 방법
      • 컴파일러에 사용되는 어떤 코드 최적화 기법들은 피연산자 평가 순서를 재순서화 함
      • 엄격한 평가 순서 정의는 컴파일러의 최적화 기법의 사용을 불허해야함
      • 따라서, 실제 언어 설계에서는 사용하기 어려움
      • JAVA는 피연산자들이 왼쪽에서 오른쪽 순서로 평가되는 것을 확정함
        1. 참조 투명성과 부작용
    • 참조 투명성 - 프로그램에서 동일한 값을 갖는 임의의 두 개 식이 프로그램의 행동에 영향을 미치지 않으면서 프로그램의 임의의 위치에서 서로 다른 식으로 대체될 수 있다면
      → 그 프로그램은 참조 투명성을 가짐
    • 투명성 - 해당 소프트웨어가 존재함에도 불구하고 인지하지 못하는 경우 투명성을 가짐

        result1 = (fun(a) + b) / (fun(a) - c)
              
        temp = fun(a)
        result2 = (temp + b) / (temp - c)
      
    • fun() 이 b, c의 값에 1을 더하는 부작용을 가지고 있으면 result1, 2의 값이 다름
      이런 부작용은 프로그램이 참조 투명성을 가지지 못하게 함
      [참고] 순수 함수형 언어들은 변수를 가지지 않기 때문에, 항상 참조 투명성을 가짐

      Untitled.png

중복 연산자

  • 흔히, 산술 연산자는 한 가지 이상의 목적으로 사용됨
    ‘+’ 연산자는 정수 덧셈과, 부동-소수점 덧셈에 사용됨, string 접합에도 사용됨

  • 한 연산자의 다중 사용을 연산자 중복이라 부름
    → 연산자 중복이 갖는 위험

  • 이항 연산자 &는 비트 AND, 단항 연산자 &는 주소 연산자
    • 완전히 관련없는 두 개의 연산에 동일한 기호를 사용함은 → 판독성에 유해
    • 첫번째 피연산자를 빠뜨리는 단순 입력 오류가 컴파일러에 의해 탐지되지 않음 → 진단하기 힘듦
  • 사용자 정의 중복
    → 추상 데이터 타입을 지원하는 언어들 중에 c++, c# 등은 프로그래머가 연산자 기호에 중복하는 것을 허용
    → 사용자 마음대로 연산자를 오버로딩 가능
    → 이는 판독성에 해로움 (’+’를 곱셈으로 정의 가능)
    → 흥미롭게도, 연산자 중복은 JAVA에서 도입되지 않음
    → 오버로딩 : parameters의 차이
    → 오버라이딩 : 재정의

타입 변환

  • 타입 변환은 축소적이나 확장적임
  • 축소 변환
    • 값을 그 원래 타입의 모든 값들의 근사치 값으로조차 저장할 수 없는 타입으로 변환
    • 축소 변환은 항상 안전하지 않음
  • 확장 변환
    • 값을 적어도 그 원래 타입의 모든 값들의 근사치를 포함할 수 있는 타입으로 변환
    • 확장 변환은 거의 안전함
  • 타입 변환은 명시적이거나 묵시적일 수 있음
    1. 식의 타입 강제 변환
    • 혼합형 식 - 4 + 0.1
      • 하나의 연산자가 서로 다른 타입의 피연산자를 가지는 식
      • 묵시적 피연산자 타입 변환을 필요로 함
    • 묵시적 타입 변환은 컴파일러에 의해 수행됨
    • 프로그래머에 의해서 명시적으로 요구되는 타입 변환은 명시적 변환 또는 캐스트라고 부름
    • 혼합형 식은 Java에서 적법한 문법으로 컴파일러는 오류로 탐지하지 않음
    • 대부분의 언어에서는 혼합형 산술식에 대한 제약 없음
      1. 명시적 타입 변환
    • 대부분의 언어에서 확장 변환, 축소 변환 모두 명시적 타입 변환 기능을 제공
    • (int)1.4, (char) etc… 이러한 명시적 타입 변환을 캐스트 라고 부름

관계식과 불리안 식

  • 관계 연산자는 항상 산술 연산자보다 더 낮은 우선 순위를 가짐 ( 괄호 사용 습관화 )
  • 불리안 연산자 - and , or , not 등등
    • C, JAVA 등 에서는 OR보다 AND가 높은 우선 순위를 가짐
  • 불리안 식 - True, False
    • C99 이전의 C는 불리안 타입을 포함하지 않음 → 정수로 참 거짓 판별
    • C 언어의 관계 연산자들은 좌결합임

단락회로 평가

  • 식의 결과가 모든 피연산자와 연산자를 평가하지 않고서 결정되는 평가

      (13 * a) * (b / 13 - 1)
      // 위 수식의 값은 a가 0이면, 뒷부분의 값에 관계없이 0으로 결정됨
      (a >= 0) && (b < 10)
      // 위 불리안 식에서 a < 0이면, 두 번째 관계식은 불리안 식의 결과와 무관함
    
  • 불리안 식에서 비 단락회로 평가가 갖는 문제점

      index = 0;
      while ((index < listlen) && (list[index] != key))
      	index = index + 1;
    
    • 평가가 단락회로가 아니면, while문의 불리안 식에 포함된 두 개의 관계식이 첫 번째 관계식의 값에 관계없이 모두 평가됨
    • 따라서, key가 list에 속하지 않으면, 프로그램은 첨자 범위 이탈 예외로 종료됨
    • 언어가 불리안 식의 단락회로 평가를 제공하면, 위의 코드는 문제 되지 않음
      → 단락회로 평가가 시행되면, 첫 번째 관계식이 거짓이면, 두 번째 관계식이 평가되지 않음
    • 단락회로 평가에 의해 발생 가능한 문제점

        (a > b) || ((b++) / 3)
        // 위 식에서 a <= b인 경우에만, b의 값이 변경됨
      
    • C, JAVA에서 &&, || 연산자는 각각 단락회로임
      python에서 모든 논리적 연산자는 단락회로 평가임

배정문

배정문은 사용자가 변수에 대한 값의 바인딩을 동적으로 변경시킬 수 있는 메커니즘을 제공

  1. 단순 배정문
    • 대부분의 언어는 배정연산자로 = 를 사용
    • 동등 관계 연산자로 == 를 사용
  2. 복합 배정 연산자
    • 배정문에서 공통적으로 필요한 형식을 명세하는 축약을 사능하게 함
    • a += b 는 a = a + b와 동등
  3. 단항 배정 연산자
    • c, java, js에서 ++, — 지원 이들은 전위 연산자, 후위 연산자로 나타낼 수 있음 (python은 없음)
    • 결합 순서는 오른쪽부터 왼쪽의 방향임을 유의 -count++ 은 -(count++)과 동일하지만 (-count)++과는 다름
  4. 식으로서의 배정문
    • c, java등 에서 배정문은 결과를 생성하는데, 그 결과는 목적지에 할당되는 값과 동일함
    • 배정 연산자는 다른 이항 연산자와 매우 유사
      단, 배정 연산자는 왼쪽 피연산자를 변경하는 부작용을 가짐
      a = b 에서 왼쪽 피연산자 a의 값을 변경

        while ((ch = getchar()) != EOF) (...)
        /*
            - 위의 문장에서, 키보드 입력 결과를 변수 ch에 배정 후
            - 주의! 배정문이 괄호 안에 표현되어야 함
                - 관계 연산자가 배정 연산자보다 우선 순위가 높기 때문
            - 배정된 값과 EOF를 비교함
        */
      
    • 이러한 식으로서의 배정문의 단점 읽고 이해하기 어려운 식을 초래할 수 있음
    • 배정문은 다중 목적지 배정의 효과를 얻을 수 있음 a = b = 0; (python도 가능)
    • 배정 연산으로 인해 오류를 초래하는 경우가 많음 JAVA, C#은 if문 안에 불리안 식만 허용, 컴파일러 또는 인터프리터가 오류를 발견

혼합형 배정문

c, c++과 같은 언어에서는 타입 강제 변환 규칙을 사용

float f = 10.3;
int i = f

Java 에서는 확장인 경우에만 타입 강제 변환을 허용

float f = 10.3f;
int i = f; // 불가능, 정보의 삭제라 판단 컴파일러가 허용하지 않음

int i = 1;
float f = i; // 가능, 하지만 컴파일러마다 error, warning 등을 출력할 수도 있음

Tags:

Categories:

Updated:

Leave a comment