Julia Language

[Julia] 기초 문법 공부 - 주요 연산자들

공부하는백수 2021. 4. 19. 00:01

※ 다음 링크에서 줄리아 프로그래밍과 관련한 글 목록을 확인하실 수 있습니다.

Julia 프로그래밍 관련 글 목록


Julia의 연산자 종류

줄리아에서는 크게 6가지 유형의 연산자가 존재한다.

  • 산술 연산자 (Arithmetic Operators)
  • 비트 연산자 (Bitwise Operators)
  • 논리 연산자 (Logical Operators) = 불리언 연산자 (Boolean Operators)
  • 대입 연산자 (Assignment Operators) = 업데이팅 연산자 (Updating Operators)
  • 벡터 도트 연산자 (Vectorized dot(.) Operators)
  • 관계 연산자 (Relational Operators) = 비교 연산자 (Comparison Operators)

산술 연산자

  산술 연산자는 크게 단항 연산자와 이항 연산자로 나눌 수 있다.

단항 연산자 (Unary Operators)

  C언어 등 기존 언어에서의 단항 연산자는 x라는 변수에 +x, -x와 같이 부호를 붙이는 정도의 역할이 전부였다. 줄리아에서는 단항 연산자를 아래와 같이 기존보다 더욱 강력하게 사용할 수 있다. 대부분의 프로그래밍 언어에서는 수학의 "2x+y" 등을 표현하는데 있어 "2"와 "x" 사이에 곱셈 기호 "*"를 사용한다. 반면, 줄리아는 곱셈 기호를 생략하고 수학식을 그대로 프로그래밍에 사용해도 된다. 이런 특성을 이용하면 아래와 같이 다항식을 보다 직관적으로 표현할 수 있다.

  아래 예제의 13번째 줄과 16번째 줄은 현실 세계에서 동일한 의미를 갖지만 줄리아 세계에서는 16번째 줄은 단항 연산자 사용 규칙을 위배하였으므로 에러를 발생시킨다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
julia> x=4
4
 
julia> +x
4
 
julia> -x
-4
 
julia> -2x
-8
 
julia> (x-1)x
12
 
julia> x(x-1# error
ERROR: MethodError: objects of type Int64 are not callable
Stacktrace:
 [1] top-level scope at none:1
 
julia> 2(x-1)^2 - 3(x-1+ 1
10
 
cs

 

이항 연산자 (Binary Operators)

  이항 연산자는 다른 언어들에서도 흔히 접할 수 있는 형태로 쓰인다. 따라서 자세한 설명보다는 아래와 같이 테이블 형태로 정리하여 설명을 대신한다. 참고로 rem(...)과 mod(...)는 연산자가 아니라 함수이지만 참고를 위해 함께 정리하였다. 그리고 mod(...) 함수의 예제 코드에서는 이 글을 읽는 사람들이 modulo 연산의 의미를 나머지 연산과 혼동하지 않기를 바라는 마음으로 음수를 입력으로 사용하였다.

연산자 의미 예제 결과
+, -, *, / 기본 사칙연산 생략 생략
a \div<tab> b 나눗셈 결과의 정수부만 반환  10 \div<tab> 3 3
a\b inverse division (a\b = b/a) 10\3 0.3
a^b 거듭제곱 (a의 b 제곱) 10^3 1000
a%b 나눗셈 연산 후 나머지 값 10%3 1
함수: rem(a,b) a%b와 동일  rem(10, 3) 1
a//b a/b를 분수 형태로 표현 10//3 10//3 (분수)
함수: mod(a,b) modulo 연산 mod(-10, 3) 2

비트 연산자

비트 연산자는 비트 단위로 AND, OR, NOT, bit shift 등을 하는 것을 의미한다. 비트 연산자와 연산자의 의미를 요약하자면 아래와 같다.

연산자 의미
~x Bitwise NOT
x & y Bitwise AND
x | y Bitwise OR
x \xor<tab> y Bitwise XOR
x >>> y Logical shift right
x >> y Arithmetic shift right
x << y Arithmetic/Logical shift left

  비트 연산자는 말 그대로 비트 단위로 연산이 이루어지므로 직접 코딩을 해가며 결과를 살펴보는 것이 이해에 가장 좋다. 이를 위해 아래와 같은 예제를 살펴보자. 아래 예제에서는 1바이트의 Int8 타입의 두 변수를 입력받아 비트 연산을 수행하고 결과를 출력하여 확인한다. AND, OR, NOT, XOR 연산은 직관적이므로 부연설명을 생략한다. 혹시 AND, OR, NOT, XOR 연산이 무엇인지 잘 모른다면 구글을 통해 먼저 공부하고 다시 이 포스트를 보는 것을 추천한다.

  Logical shift와 arithmetic shift 모두 bitwise shift이다. 그렇다면 둘의 차이점은 무엇일까? 아래 예제에서 24번째 줄과 27번째 줄의 연산 결과가 어떻게 다른지 살펴보면 둘이 차이가 무엇인지 알 수 있다. 그렇다. Logical shift 부호비트 정보를 무시하지만 arithmetic shift는 부호비트 정보를 보존한다. 참고로 bitstring 함수는 입력 값을 비트스트림 문자열로 변환해주는 함수이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
julia> x = Int8(-20); y = Int8(7);
 
julia> bitstring(x)
"11101100"
 
julia> bitstring(y)
"00000111"
 
julia> bitstring(~x)
"00010011"
 
julia> bitstring(x&y)
"00000100"
 
julia> bitstring(x|y)
"11101111"
 
julia> bitstring(x|y)
"11101111"
 
julia> bitstring(x⊻y)
"11101011"
 
julia> bitstring(x>>>2)
"00111011"
 
julia> bitstring(x>>2)
"11111011"
 
julia> bitstring(x<<2)
"10110000"
cs

논리 연산자 (불리언 연산자)

  프로그래밍 경험이 있는 사람이라면 별도의 설명이 필요 없을 정도로 논리 연산자는 매우 간단하다. 논리적 AND, OR, NOT 연산을 수행하는 연산자인데, 앞에서 살펴본 비트 연산자의 AND, OR, NOT은 비트 단위로 연산이 이루어지지만 논리 연산자는 입력 인자들의 관계를 비교하여 true or false를 반환한다.

연산자 의미
&& Logical AND
|| Logical OR
! Logical NOT

대입 연산자 (업데이팅 연산자)

  대입연산자(혹은 업데이팅 연산자)는 변수의 값을 할당하거나 갱신하는데 사용된다. 우리는 너무나 자연스럽게 사용하지만 "="도 사실 우변에 있는 값을 좌변에 할당하는 가장 대표적인 대입 연산자이다.

  "=" 연사자를 제외하면 나머지 대입 연산자는 사실 "+=", "-=" 등과 같이 이항연산자 혹은 비트연산자에 "="가 결합된 형태(편의상 "?="라고 부르자)로 정의된다. 이러한 표현의 의미는 좌변과 우변의 값으로 "?" 연산을 수행하고 연산 결과를 좌변에 다시 대입하라는 의미이다. 즉, 좌변의 변수에 들어있는 값을 갱신하는 것으로 updating operator라고 부르기도 한다. 다음은 줄리아에서 활용가능한 대입 연산자 목록이다.

+=  -=  *=  /=  \=  ÷=  %=  ^=  &=  |=  ⊻=  >>>=  >>=  <<=

  아래는 산술 연산자 기반의 대입 연산자 활용 예이다. 비트 연산자를 활용한 대입 연산자들은 비트 연산자를 복습할 겸 직접 테스트해기로 한다. 그리고 두 번째 줄에 사용한 "$"는 문자열에서의 보간이라는 것이다. 해당 내용이 익숙치 않다면 여기를 참고하면 도움이 될 것이다. 참고로 아래 예제에서는 생략되었지만 "x=y=z=1"과 같이 multiple assignment 기능도 지원된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
julia> a = 3; b = 5;
julia> println("a = $a, b = $b")
= 8, b = 5
 
julia> println("a += b = ", a += b);
+= b = 8
 
julia> a = 3; b = 5; println("a -= b = ", a -= b)
-= b = -2
 
julia> a = 3; b = 5; println("a *= b = ", a *= b)
*= b = 15
 
julia> a = 3; b = 5; println("a /= b = ", a /= b)
/= b = 0.6
 
julia> a = 3; b = 5; println("a \\= b = ", a \= b)
a \= b = 1.6666666666666667
 
julia> a = 3; b = 5; println("a ^= b = ", a ^= b)
a ^= b = 243
 
julia> a = 3; b = 5; println("a %= b = ", a %= b)
a %= b = 3
 
cs

벡터 도트 연산자

  이 연산자는 element-wise로 array 연산을 수행하고자 할 때 사용하며 "."라고 표기한다. MATLAB에서 벡터와 행렬 등을 다룰 때 element-wise operation을 하기 위해 사용했던 "."와 완전히 동일하다. 즉, [1, 2], [3, 4]라는 두 벡터가 있을 때 [1, 2] .* [3, 4] = [1*3, 2*4] = [3, 8]이 된다. 도트 연산자는 함수와도 함께 사용할 수 있다. 구체적인 활용 예를 아래 예제를 통해 살펴보자. 참고로 Julia는 벡터나 행렬을 선언할 때 "," 혹은 ";"을 사용하여 행을 구분하고, 공백으로 열을 구분한다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
julia> # [1 2 3]^2 # error
 
julia> [1 2 3].^2
1×3 Array{Int64,2}:
 1  4  9
 
julia> [1 2].*[4 3]
1×2 Array{Int64,2}:
 4  6
 
julia> uppercase.(["i""love""julia"])
3-element Array{String,1}:
 "I"
 "LOVE"
 "JULIA"
 
julia> abs.([-1020-5])
3-element Array{Int64,1}:
 10
 20
  5
 
cs

  참고로 MATLAB에서는 x라는 벡터가 있을 때 1./x라는 연산이 가능하지만 줄리아에서는 1./x는 에러가 발생하고 1/x는 에러 없이 동작한다. 그러나 1/x의 연산 결과는 본래 의도했던 결과와 다르다. 이런 경우에는 x.\1과 같이 inverse division과 벡터 도트 연산자를 함께 사용하여야 한다. 반면, x라는 벡터와 y라는 벡터 간의 element-wise division을 하고 싶다면 x./y와 같이 반드시 벡터 도트 연산자를 사용해주어야 한다. 

  벡터 도트 연산자는 브로드 캐스팅에서도 사용된다. x라는 벡터(혹은 N차원 어레이)의 각 요소에 동일한 스칼라 값을 더할 때 반드시 x .+ 1.0과 같은 식으로 벡터 도트 연산자를 사용하여야 한다.


관계 연산자

  관계연산자는 비교연산자라고도 하는데 변수 간의 값이 같거나 다른지, 크거나 작은지를 체크할 때 사용한다. 간단히 표로 정리하자면 다음과 같다. "!=", "<=", ">=" 연산자는 Latex 구문도 함께 지원하는데 최적화 문제의 목적식과 제약식 등을 보다 직관적으로 쓰는데 유용하다. 줄리아에서는 관계 연산자를 이용하여 문자열 비교 연산을 수행한다. 관련 내용이 궁금하다면 여기를 참고하길 추천한다.

연산자 의미
== equality
!=, \ne<tab> inequality
< less than
<=, \le<tab> less than or equal to
> greater than
>=, \ge<tab> greater than or equal to

참고로 Julia 문법 중 "==="라는 연산자도 있는데 "==="와 "=="의 차이는 다음과 같다.

  • "x == y": equality check, 두 객체 x, y가 갖는 "값"을 갖고 있으면 true (서로 다른 객체여도 값만 갖으면 true)
  • "x === y": identity check, 두 객체 x, y가 동일한 객체일 때 true

관계 연산자들이 제공하는 역할이 함수형태로도 제공된다. 대표적으로 isequal(x, y), isfinite(x), isinf(x), isnan(x) 등이 있다.