변수

변수 (Variables)

Julia에서 변수(variable)는 값에 이름을 붙이는(묶는, bind) 개념이에요. 예를 들어 어떤 계산으로 얻은 값을 나중에 쓰기 위해 저장해 두고 싶을 때 유용하죠. 다음 예시를 볼게요.

julia> x = 10   # Assign the value 10 to the variable x
10

julia> x + 1    # Doing math with x's value
11

julia> x = 1 + 1   # Reassign x's value
2

julia> x = "Hello World!"   # You can assign values of other types, like strings of text
"Hello World!"

Julia는 변수 이름 짓기에 아주 유연한 체계를 제공해요. 변수 이름은 대소문자를 구분하며, 의미(semantic)를 갖지 않아요 — 즉 언어가 이름에 따라 변수를 다르게 취급하지 않는다는 뜻이에요.

julia> x = 1.0
1.0

julia> y = -3
-3

julia> Z = "My string"
"My string"

julia> customary_phrase = "Hello world!"
"Hello world!"

julia> UniversalDeclarationOfHumanRightsStart = "人人生而自由,在尊严和权利上一律平等。"
"人人生而自由,在尊严和权利上一律平等。"

유니코드 이름(UTF-8 인코딩)도 허용돼요.

julia> δ = 0.00001
1.0e-5

julia> 안녕하세요 = "Hello"
"Hello"

Julia REPL과 여러 Julia 편집 환경에서는 LaTeX 심볼 이름을 백슬래시로 입력한 뒤 탭을 누르는 방식으로 많은 유니코드 수학 심볼을 입력할 수 있어요. 예를 들어 변수 이름 δ\delta-으로 입력할 수 있고, α̂⁽²⁾\alpha--\hat--\^(2)-으로 입력할 수 있어요. (다른 사람 코드에서 심볼을 발견했는데 입력 방법을 모르겠다면 REPL 도움말이 알려줘요 — ?를 입력한 뒤 그 심볼을 붙여넣으면 됩니다.)

Julia는 심지어 기존에 export된 상수·함수를 지역(local) 것으로 가릴 수 있게 해 줘요 (혼란을 막기 위해 권장되진 않지만요).

julia> pi = 3
3

julia> pi
3

julia> sqrt = 4
4

julia> length() = 5
length (generic function with 1 method)

julia> Base.length
length (generic function with 79 methods)

하지만 명시적으로 import한 내장 상수·함수를 재정의하려 하면 Julia는 오류를 반환해요.

julia> using Base: pi, sqrt

julia> pi
π = 3.1415926535897...

julia> pi = 3
ERROR: cannot assign a value to imported variable Base.pi from module Main

julia> sqrt(100)
10.0

julia> sqrt = 4
ERROR: cannot assign a value to imported variable Base.sqrt from module Main

compat — Julia 1.12: Julia 1.12보다 이전 버전에서는 이런 오류가 충돌하는 바인딩의 정의가 아니라 사용에 의존했어요.

출처: Julia 공식 매뉴얼 - Variables

허용 가능한 변수 이름 (Allowed Variable Names)

변수 이름은 문자(A-Z 또는 a-z), 밑줄(underscore), 또는 00A0보다 큰 유니코드 코드포인트의 부분집합으로 시작해야 해요. 구체적으로는 유니코드 문자 카테고리 중 Lu/Ll/Lt/Lm/Lo/Nl(문자), Sc/So(통화 및 기타 기호), 그리고 몇몇 다른 문자 비슷한 글자(예: Sm 수학 기호의 부분집합)가 허용돼요. 이어지는 문자에는 !와 숫자(0-9 및 Nd/No 카테고리의 기타 문자), 그리고 다른 유니코드 코드포인트들 — 발음 구별 기호 등 수정 표시(Mn/Mc/Me/Sk 카테고리), 일부 구두점 연결자(Pc 카테고리), 프라임(prime), 기타 몇몇 문자 — 도 쓸 수 있어요.

+ 같은 [연산자](@ref Operator-Precedence-and-Associativity)도 유효한 식별자이지만 특별히 파싱돼요. 어떤 문맥에서는 연산자를 변수처럼 쓸 수도 있어요. 예를 들어 (+)는 덧셈 함수를 가리키고, (+) = f는 그것을 재할당해요. 처럼 Sm 카테고리의 대부분의 유니코드 중위(infix) 연산자는 중위 연산자로 파싱되어 사용자 정의 메서드에 쓸 수 있어요(예: const ⊗ = kron으로 를 중위 크로네커 곱으로 정의할 수 있어요). 연산자에는 수정 표시, 프라임, 아래/위 첨자도 붙일 수 있어요. 예를 들어 +̂ₐ″+와 같은 우선순위를 갖는 중위 연산자로 파싱돼요. 아래/위 첨자 문자로 끝나는 연산자와 그 뒤에 오는 변수 이름 사이에는 공백이 필요해요. 예를 들어 +ᵃ가 연산자라면 +ᵃx+ ᵃx(여기서 ᵃx는 변수 이름)와 구분하기 위해 +ᵃ x로 써야 해요.

밑줄만으로 이루어진 변수 이름은 특별한 부류로, 이런 식별자는 쓰기 전용(write-only) 이에요. 즉 값만 할당할 수 있고, 그 값은 즉시 버려지며 그 값을 어떤 방식으로도 사용할 수 없어요.

julia> x, ___ = size([2 2; 1 1])
(2, 2)

julia> y = ___
ERROR: syntax: all-underscore identifiers are write-only and their values cannot be used in expressions

julia> println(___)
ERROR: syntax: all-underscore identifiers are write-only and their values cannot be used in expressions

명시적으로 금지된 유일한 변수 이름은 내장 [키워드](@ref Keywords)의 이름들이에요.

julia> else = false
ERROR: ParseError:
# Error @ none:1:1
else = false
└──┘ ── invalid identifier

julia> try = "No"
ERROR: ParseError:
# Error @ none:1:1
try = "No"
└────────┘ ── try without catch or finally

일부 유니코드 문자는 식별자 안에서 서로 동등한 것으로 간주돼요. 유니코드 결합 문자(combining character, 예: 악센트)를 입력하는 서로 다른 방식은 동등하게 취급돼요(구체적으로 Julia 식별자는 NFC 정규화돼요). Julia는 또한 시각적으로 비슷하고 일부 입력 방식으로 쉽게 입력되는 문자들에 대해 몇 가지 비표준 동치도 포함해요. ɛ(U+025B: Latin small letter open e)와 µ(U+00B5: micro sign)는 그에 대응하는 그리스 문자와 동등하게 취급돼요. 가운뎃점 ·(U+00B7)과 그리스 인터펑크트 ·(U+0387)는 둘 다 수학적 점 연산자 (U+22C5)로 취급돼요. 마이너스 기호 (U+2212)는 하이픈-마이너스 기호 -(U+002D)와 동등하게 취급돼요.

할당 표현식과 할당 대 수정 (Assignment expressions and assignment versus mutation)

할당 variable = value는 이름 variable을 오른쪽에서 계산된 value에 "묶으며", 그 할당 전체는 Julia에서 오른쪽 value와 같은 값을 갖는 표현식(expression) 으로 취급돼요. 이 말은 할당을 연쇄(variable1 = variable2 = value처럼 같은 value를 여러 변수에 할당)할 수 있고 다른 표현식 안에서도 쓸 수 있으며, 그 결과가 REPL에 오른쪽 값으로 표시되는 이유이기도 해요. (일반적으로 REPL은 평가한 표현식의 값을 표시해요.) 예를 들어 여기서 b = 2+2의 값 4는 다른 산술 연산과 할당에 사용돼요.

julia> a = (b = 2+2) + 3
7

julia> a
7

julia> b
4

흔히 헷갈리는 지점이 할당(값에 새 "이름"을 붙이는 것)과 수정(값을 바꾸는 것)의 차이예요. a = 2를 실행하고 이어서 a = 3을 실행했다면, 이름 a가 새 값 3을 가리키도록 바꾼 거예요 — 숫자 2 자체를 바꾼 게 아니므로 2+2는 여전히 4를 주지 6을 주지 않아요! 이 구분은 [배열](@ref lib-arrays)처럼 내용을 바꿀 수 있는 가변(mutable) 타입을 다룰 때 더 명확해져요.

julia> a = [1,2,3] # an array of 3 integers
3-element Vector{Int64}:
 1
 2
 3

julia> b = a   # both b and a are names for the same array!
3-element Vector{Int64}:
 1
 2
 3

여기서 b = a는 배열 a의 복사본을 만드는 게 아니라 이름 b같은 배열 a에 묶을 뿐이에요. ba 둘 다 메모리 속의 한 배열 [1,2,3]을 "가리키고" 있죠. 반면 a[i] = value는 배열의 내용바꾸며, 수정된 배열은 ab 두 이름 모두에서 보여요.

julia> a[1] = 42     # change the first element
42

julia> a = 3.14159   # a is now the name of a different object
3.14159

julia> b   # b refers to the original array object, which has been mutated
3-element Vector{Int64}:
 42
  2
  3

a[i] = value(setindex!의 별칭)는 ab 어느 쪽으로든 접근할 수 있는, 메모리 속의 기존 배열 객체를 수정해요. 그 다음에 a = 3.14159로 설정하는 것은 이 배열을 바꾸지 않고 단지 a를 다른 객체에 묶을 뿐이에요 — 배열은 여전히 b로 접근할 수 있죠. 기존 객체를 수정하는 또 다른 흔한 구문은 a.field = value(setproperty!의 별칭)로, mutable struct를 바꾸는 데 쓸 수 있어요. 점 할당(dot assignment)을 통한 수정도 있어요. 예를 들어 b .= 5:7은 배열 b를 제자리에서 [5,6,7]로 수정하며, 이는 Julia의 [벡터화된 "dot" 구문](@ref man-dot-operators)의 일부예요.

Julia에서 [함수](@ref man-functions)를 호출하면, [인자 전달 동작](@ref man-argument-passing)에서 논의한 대로 인자 값을 함수 인자에 해당하는 새 변수 이름에 할당한 것처럼 동작해요. (관례상으로, 인자 하나 이상을 수정하는 함수는 이름이 !로 끝나요.)

스타일 관례 (Stylistic Conventions)

Julia는 유효한 이름에 제약을 거의 두지 않지만, 다음과 같은 관례를 따르는 것이 유용하게 여겨져요.

  • 변수 이름은 소문자로 써요.
  • 단어 구분은 밑줄('_')로 나타낼 수 있지만, 이름을 그렇게 하지 않으면 읽기 어려운 경우가 아니라면 밑줄 사용은 권장되지 않아요.
  • TypeModule의 이름은 대문자로 시작하고, 단어 구분은 밑줄 대신 대문자 카멜 케이스로 표시해요.
  • functionmacro의 이름은 소문자로, 밑줄 없이 써요.
  • 인자에 값을 쓰는(write) 함수는 이름이 !로 끝나요. 이런 함수는 값을 반환할 뿐 아니라 함수 호출 후 인자에 변화를 만들기 위한 것이므로 "mutating"(수정용) 또는 "in-place"(제자리) 함수라고 부르기도 해요.

스타일 관례에 대한 더 자세한 내용은 스타일 가이드를 참고하세요.

더 알아보기 (Learn more)