Expressions and Assignment Statements
Chapter7 - Expressions and Assignment Statements
산술식
- 프로그래밍 언어에서의 산술식 구성 요소
- 연산자
- 피연산자
- 괄호
- 함수 호출
- 연산자의 종류
- 단항
- 이항
- 삼항 - 세 개의 피연산자를 가짐
- 이항 연산자의 표기법
- 대부분의 명령형 프로그래밍 언어에서 중위 표기법
- perl 과 같은 언어에서는 전위 표기법을 사용
- 연산자 우선순위와 결합규칙에 따라 평가(계산) 순서가 결정됨
- 우선순위
연산자 우선순위 규칙은 서로 다른 우선순위 수준을 갖는 연산자들이 평가되는 순서를 부분적으로 정의함 - 덧셈과 뺄셈의 단항 버전
많은 언어들에서 지원
단항 덧셈을 항등 연산자라 부름
→ 어떠한 영향도 주지 않는 연산자이기 때문 단항 뺄셈은 그 연산자의 부호를 변경하는 연산자(단, 괄호 사용이 필요) -
다음 식들에서의 연산자 우선순위
- x / y // - x * y // - x ** y // 지수 계산
위의 식에서 단항 뺄셈과 이항 연산자간의 상대적 우선순위가 중요하지 않음
3번째 에서는 단항 뺄셈과 이항 연산자(**)간의 상대적 우선 순위가 중요함
→ 언어들마다 산술 연산자의 우선순위를 정의하고 있음
- 결합규칙
- 덧셈과 뺄셈 연산자가 동일한 우선순위를 갖는다면, 우선순위 규칙은 위의 식의 평가 순서에 대하여 아무것도 말해주지 않은 것임
- 식이 동일한 수준의 우선순위를 가지면서, 인접된 두 개의 연사자를 포함할 때, 어느 연산자가 먼저 평가될 것인가에 대한 규칙이 결합 규칙임
- 공통된 언어들의 결합 규칙
- 왼쪽 부터 오른쪽 순서
- 예외 : 지수 연산자 (오른쪽 에서 왼쪽 순서) → C, JAVA는 제공안함
- 괄호
- 프로그래머가 식에 괄호를 포함시켜 우선순위 규칙과 결합규칙을 변경할 수 있음
- 조건식
- if-then-else 문이 조건식 배정문을 수행하는데 사용될 수 있음
- C와 JAVA에서는 삼항연산자로 동일한 조건식 표현 가능 (A ? B : C)
- python의 경우 (A if B else C)로 표현 가능
피연산자 평가 순서
- 어떤 피연산자도 부작용을 갖지 않으면, 피연산자의 평가 순서는 무관함
- 따라서, 피연산자의 평가가 부작용을 가지는 경우에 대한 고려가 필요함
- 부작용
- 함수의 부작용 (함수적 부작용)
함수가 자신의 매개변수들 중의 하나의 값이나 전역 변수의 값을 변경할 때 발생함 - 예제 식 : 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는 피연산자들이 왼쪽에서 오른쪽 순서로 평가되는 것을 확정함
- 참조 투명성과 부작용
- 참조 투명성 - 프로그램에서 동일한 값을 갖는 임의의 두 개 식이 프로그램의 행동에 영향을 미치지
않으면서 프로그램의 임의의 위치에서 서로 다른 식으로 대체될 수 있다면
→ 그 프로그램은 참조 투명성을 가짐 -
투명성 - 해당 소프트웨어가 존재함에도 불구하고 인지하지 못하는 경우 투명성을 가짐
result1 = (fun(a) + b) / (fun(a) - c) temp = fun(a) result2 = (temp + b) / (temp - c)
-
fun() 이 b, c의 값에 1을 더하는 부작용을 가지고 있으면 result1, 2의 값이 다름
이런 부작용은 프로그램이 참조 투명성을 가지지 못하게 함
[참고] 순수 함수형 언어들은 변수를 가지지 않기 때문에, 항상 참조 투명성을 가짐
중복 연산자
-
흔히, 산술 연산자는 한 가지 이상의 목적으로 사용됨
‘+’ 연산자는 정수 덧셈과, 부동-소수점 덧셈에 사용됨, string 접합에도 사용됨 -
한 연산자의 다중 사용을 연산자 중복이라 부름
→ 연산자 중복이 갖는 위험 - 이항 연산자 &는 비트 AND, 단항 연산자 &는 주소 연산자
- 완전히 관련없는 두 개의 연산에 동일한 기호를 사용함은 → 판독성에 유해
- 첫번째 피연산자를 빠뜨리는 단순 입력 오류가 컴파일러에 의해 탐지되지 않음 → 진단하기 힘듦
- 사용자 정의 중복
→ 추상 데이터 타입을 지원하는 언어들 중에 c++, c# 등은 프로그래머가 연산자 기호에 중복하는 것을 허용
→ 사용자 마음대로 연산자를 오버로딩 가능
→ 이는 판독성에 해로움 (’+’를 곱셈으로 정의 가능)
→ 흥미롭게도, 연산자 중복은 JAVA에서 도입되지 않음
→ 오버로딩 : parameters의 차이
→ 오버라이딩 : 재정의
타입 변환
- 타입 변환은 축소적이나 확장적임
- 축소 변환
- 값을 그 원래 타입의 모든 값들의 근사치 값으로조차 저장할 수 없는 타입으로 변환
- 축소 변환은 항상 안전하지 않음
- 확장 변환
- 값을 적어도 그 원래 타입의 모든 값들의 근사치를 포함할 수 있는 타입으로 변환
- 확장 변환은 거의 안전함
- 타입 변환은 명시적이거나 묵시적일 수 있음
- 식의 타입 강제 변환
- 혼합형 식 - 4 + 0.1
- 하나의 연산자가 서로 다른 타입의 피연산자를 가지는 식
- 묵시적 피연산자 타입 변환을 필요로 함
- 묵시적 타입 변환은 컴파일러에 의해 수행됨
- 프로그래머에 의해서 명시적으로 요구되는 타입 변환은 명시적 변환 또는 캐스트라고 부름
- 혼합형 식은 Java에서 적법한 문법으로 컴파일러는 오류로 탐지하지 않음
- 대부분의 언어에서는 혼합형 산술식에 대한 제약 없음
- 명시적 타입 변환
- 대부분의 언어에서 확장 변환, 축소 변환 모두 명시적 타입 변환 기능을 제공
- (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에서 모든 논리적 연산자는 단락회로 평가임
배정문
배정문은 사용자가 변수에 대한 값의 바인딩을 동적으로 변경시킬 수 있는 메커니즘을 제공
- 단순 배정문
- 대부분의 언어는 배정연산자로 = 를 사용
- 동등 관계 연산자로 == 를 사용
- 복합 배정 연산자
- 배정문에서 공통적으로 필요한 형식을 명세하는 축약을 사능하게 함
- a += b 는 a = a + b와 동등
- 단항 배정 연산자
- c, java, js에서 ++, — 지원 이들은 전위 연산자, 후위 연산자로 나타낼 수 있음 (python은 없음)
- 결합 순서는 오른쪽부터 왼쪽의 방향임을 유의 -count++ 은 -(count++)과 동일하지만 (-count)++과는 다름
- 식으로서의 배정문
- 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 등을 출력할 수도 있음
Leave a comment