다른 언어와의 주목할 만한 차이점

다른 언어와의 주목할 만한 차이점 (Noteworthy Differences from other Languages)

다른 언어를 쓰다 온 사람이라면 Julia가 낯설지만 친숙한 구석이 많아요. 특히 MATLAB, R, Python, C/C++, Common Lisp 사용자들이 흔히 부딪히는 차이점을 정리했어요. 각 언어별로 어떤 점이 다른지 짚어볼게요.

출처: Julia 공식 매뉴얼 · Noteworthy Differences from other Languages

본문

MATLAB과의 주목할 만한 차이점

MATLAB 사용자라면 Julia의 문법이 익숙하게 느껴질 테지만, Julia는 MATLAB의 복제판이 아니에요. 문법적·기능적으로 큰 차이가 있습니다. MATLAB에 익숙한 Julia 사용자를 골탕 먹일 수 있는 주목할 만한 차이점들은 이래요.

  • Julia 배열은 대괄호로 인덱싱해요. A[i,j].
  • Julia 배열은 다른 변수에 할당할 때 복사되지 않아요. A = B 뒤에 B의 요소를 바꾸면 A도 함께 바뀌어요. 피하려면 A = copy(B)를 쓰세요.
  • Julia 값은 함수에 전달할 때 복사되지 않아요. 함수가 배열을 수정하면 그 변경이 호출자에게 보여요.
  • Julia는 할당문에서 배열을 자동으로 늘리지 않아요. MATLAB에서 a(4) = 3.2는 배열 a = [0 0 0 3.2]를 만들 수 있고 a(5) = 7a = [0 0 0 3.2 7]로 늘릴 수 있는 반면, Julia에서는 a의 길이가 5보다 작거나 문장이 식별자 a의 첫 사용이라면 상응하는 a[5] = 7이 오류를 던져요. Julia에는 push!append!가 있는데, 이것들은 MATLAB의 a(end+1) = val보다 훨씬 효율적으로 Vector를 늘려줘요.
  • 허수 단위 sqrt(-1)는 Julia에서 im으로 표현돼요. MATLAB처럼 ij가 아니에요.
  • Julia에서 소수점이 없는 리터럴 숫자(예: 42)는 부동소수점 숫자가 아니라 정수를 만들어요. 그 결과 어떤 연산은 부동소수점을 기대하면 도메인 오류(domain error)를 던질 수 있어요. 예를 들어 julia> a = -1; 2^a는 결과가 정수가 아니므로 도메인 오류를 던져요(자세한 내용은 도메인 오류에 관한 FAQ 항목 참조).
  • Julia에서 여러 값은 튜플로 반환·할당돼요. 예를 들어 (a, b) = (1, 2)a, b = 1, 2처럼요. MATLAB에서 반환된 값의 개수에 따라 선택적 작업을 하는 데 자주 쓰이는 nargout은 Julia에 없어요. 대신 선택적 인자와 키워드 인자로 비슷한 능력을 얻을 수 있어요.
  • Julia에는 진짜 1차원 배열이 있어요. 열 벡터는 Nx1이 아니라 크기가 N이에요. 예를 들어 rand(N)은 1차원 배열을 만들어요.
  • Julia에서 [x,y,z]는 항상 x, y, z를 담은 3-요소 배열을 만들어요. 첫 번째("세로") 차원으로 연결하려면 vcat(x,y,z)를 쓰거나 세미콜론으로 구분하면 돼요([x; y; z]).
  • 두 번째("가로") 차원으로 연결하려면 hcat(x,y,z)를 쓰거나 공백으로 구분하면 돼요([x y z]).
  • 블록 행렬(첫 두 차원에서 연결)을 만들려면 hvcat을 쓰거나 공백과 세미콜론을 조합하면 돼요([a b; c d]).
  • Julia에서 a:ba:b:cAbstractRange 객체를 만들어요. MATLAB처럼 완전한 벡터를 만들려면 collect(a:b)를 쓰세요. 일반적으로 collect를 호출할 필요는 없어요. AbstractRange 객체는 대부분의 경우 일반 배열처럼 동작하지만 값을 지연 계산해서 더 효율적이에요. 완전한 배열 대신 특수화된 객체를 만드는 이 패턴은 자주 쓰이며, range 같은 함수나 enumerate, zip 같은 이터레이터에서도 볼 수 있어요. 그 특수 객체들은 대부분 일반 배열처럼 쓸 수 있어요.
  • Julia 함수는 함수 정의에서 반환할 변수 이름을 나열하는 대신 마지막 표현식이나 return 키워드에서 값을 반환해요(return 키워드에 대한 자세한 내용 참조).
  • Julia 스크립트는 얼마든지 많은 함수를 담을 수 있고, 파일이 로드되면 모든 정의가 외부에서 보여요. 함수 정의는 현재 작업 디렉터리 밖의 파일에서도 불러올 수 있어요.
  • Julia에서 sum, prod, maximum 같은 축소(reduction)는 단일 인자로 호출하면 sum(A)처럼 배열의 모든 요소에 대해 수행돼요. A가 여러 차원이어도 마찬가지예요.
  • Julia에서는 인자가 없는 함수를 호출하려면 rand()처럼 괄호를 써야 해요.
  • Julia는 문장을 끝내기 위해 세미콜론을 쓰는 것을 권장하지 않아요. 문장 결과는 (대화형 프롬프트를 제외하고) 자동으로 출력되지 않고, 코드 줄을 세미콜론으로 끝낼 필요도 없어요. 특정 출력을 내려면 println이나 @printf를 쓸 수 있어요.
  • Julia에서 AB가 배열이라면, A == B 같은 논리 비교 연산은 불리언 배열을 반환하지 않아요. 대신 A .== B를 쓰고, 다른 불리언 연산자도 마찬가지예요.
  • Julia에서 스칼라 값을 반환하는 함수를 배열에 원소별로 적용하고 싶다면 브로드캐스팅 문법 f.(A)f(A) 대신 쓰세요. 어떤 경우에는 두 연산이 모두 정의되지만 뜻이 달라요. MATLAB에서 exp(A)는 원소별로 적용하고 expm(A)는 행렬 지수이지만, Julia에서 exp.(A)는 원소별로 적용하고 exp(A)는 행렬 지수예요.
  • Julia에서 연산자 &, |, (xor)는 MATLAB의 and, or, xor에 각각 해당하는 비트 연산을 수행하고, (C와 달리) Python의 비트 연산자와 비슷한 우선순위를 가져요. 배열에 논리 불리언 연산자를 적용하려면(MATLAB의 &·|의 흔한 용법처럼) Julia의 단락 연산자 .&&.||를 브로드캐스트하세요. 예를 들어 배열 A의 요소가 1이나 2와 같은지 테스트하려면 A .== 1 .|| A .== 2를 쓸 수 있어요.
  • Julia에서는 컬렉션의 요소를 스플랫 연산자 ...로 함수에 인자로 전달할 수 있어요. xs=[1,2]; f(xs...)처럼 요.
  • Julia의 svd는 특이값을 조밀한 대각 행렬이 아니라 벡터로 반환해요.
  • Julia에서 ...는 코드 줄을 이어 가는 데 쓰이지 않아요. 대신 불완전한 표현식은 자동으로 다음 줄로 계속돼요.
  • Julia와 MATLAB 둘 다에서 변수 ans는 대화형 세션에서 마지막으로 발행한 표현식의 값으로 설정돼요. MATLAB과 달리 Julia에서는 비대화형 모드로 Julia 코드를 실행할 때 ans가 설정되지 않아요.
  • Julia의 struct는 MATLAB의 클래스와 달리 런타임에 필드를 동적으로 추가하지 못해요. 대신 Dict를 쓰세요. Julia의 Dict는 순서가 없어요.
  • Julia에서는 각 모듈이 자기만의 전역 스코프/네임스페이스를 가지지만, MATLAB에는 전역 스코프가 하나뿐이에요.
  • MATLAB에서 원하지 않는 값을 제거하는 관용적 방법은 x(x>3) 표현식이나 원위치에서 x를 수정하는 x(x>3) = [] 문장처럼 논리 인덱싱을 쓰는 거예요. 반대로 Julia는 고차 함수 filterfilter!를 제공해서, 사용자들이 대응되는 직역 x[x.>3]x = x[x.>3]의 대안으로 filter(z->z>3, x)filter!(z->z>3, x)를 쓸 수 있게 해줘요. filter!를 쓰면 임시 배열 사용이 줄어요.
  • 앞의 요점에 이어서, 특정 기준을 충족하는 값을 바꾸는 데, 예를 들어 행렬의 모든 요소에 임계값(thresholding) 연산을 하는 것은 MATLAB에서 A(A < threshold) = 0으로 할 수 있어요. Julia에서는 A[A .< threshold] .= 0이 상응해요.
  • 셀 배열의 모든 요소를 추출(혹은 "역참조")하는 것의 유사어, 예를 들어 MATLAB의 vertcat(A{:})은, Julia에서는 스플랫 연산자로 써요. vcat(A...)처럼 요.
  • Julia에서 adjoint 함수는 켤레 전치(conjugate transposition)를 수행해요. 반면 MATLAB에서 adjoint는 "수반(adjugate)" 또는 고전 수반을 제공하는데, 그것은 여인자 행렬의 전치예요.
  • Julia에서 a^b^ca^(b^c)로 평가되지만, MATLAB에서는 (a^b)^c예요.

R과의 주목할 만한 차이점

Julia의 목표 중 하나는 데이터 분석과 통계 프로그래밍을 위한 효과적인 언어를 제공하는 거예요. R에서 Julia로 오는 사용자를 위한 주목할 만한 차이점은 이렇습니다.

  • Julia의 작은따옴표는 문자열이 아니라 문자(character)를 감싸요.
  • Julia는 문자열을 인덱싱해 부분 문자열을 만들 수 있어요. R에서는 부분 문자열을 만들기 전에 문자열을 문자 벡터로 변환해야 해요.
  • Julia에서는 Python처럼, R과 달리, 삼중 따옴표 """ ... """로 문자열을 만들 수 있어요. 이 문법은 줄바꿈을 포함하는 문자열을 만들 때 편리해요.
  • Julia에서 가변 인자(varargs)는 스플랫 연산자 ...로 지정하는데, 항상 특정 변수의 이름 뒤에 옵니다. R처럼 ...가 단독으로 나올 수는 없어요.
  • Julia에서 나머지(modulus)는 mod(a, b)이지 a %% b가 아니에요. Julia에서 %는 나머지(remainder) 연산자예요.
  • Julia는 벡터를 대괄호로 구성해요. Julia의 [1, 2, 3]은 R의 c(1, 2, 3)과 같아요.
  • Julia에서 모든 데이터 구조가 논리 인덱싱을 지원하는 건 아니에요. 게다가 Julia의 논리 인덱싱은 인덱싱되는 객체와 길이가 같은 벡터로만 지원돼요. 예를 들어 R에서 c(1, 2, 3, 4)[c(TRUE, FALSE)]c(1, 3)과 같아요.
  • R에서 c(1, 2, 3, 4)[c(TRUE, FALSE, TRUE, FALSE)]c(1, 3)과 같아요.
  • Julia에서 [1, 2, 3, 4][[true, false]]BoundsError를 던져요.
  • Julia에서 [1, 2, 3, 4][[true, false, true, false]][1, 3]을 만들어요.
  • 많은 언어처럼 Julia는 항상 길이가 다른 벡터에 연산을 허용하지는 않아요. R에서는 벡터들이 공통 인덱스 범위만 공유하면 됐죠. 예를 들어 c(1, 2, 3, 4) + c(1, 2)는 유효한 R이지만, 그에 해당하는 [1, 2, 3, 4] + [1, 2]는 Julia에서 오류를 던져요.
  • Julia는 콤마가 코드의 의미를 바꾸지 않을 때 선택적 trailing 콤마를 허용해요. 이는 배열을 인덱싱할 때 R 사용자에게 혼란을 줄 수 있어요. 예를 들어 R의 x[1,]는 행렬의 첫 행을 반환하지만, Julia에서는 콤마가 무시되므로 x[1,] == x[1]이고 첫 요소를 반환해요. 행을 추출하려면 x[1,:]처럼 :를 써야 해요.
  • Julia의 map은 R의 lapply(<structure>, function, ...)과 달리 함수를 먼저 받고, 그다음 인자를 받아요. 마찬가지로 R의 apply(X, MARGIN, FUN, ...)에 해당하는 Julia의 것은 mapslices인데, 함수가 첫 인자예요.
  • R의 다변량 apply, 예를 들어 mapply(choose, 11:13, 1:3)은 Julia에서 broadcast(binomial, 11:13, 1:3)으로 쓸 수 있어요. 동등하게 Julia는 함수를 벡터화하는 더 짧은 점(dot) 문법 binomial.(11:13, 1:3)을 제공해요.
  • Julia는 if 같은 조건 블록, while/for 같은 루프 블록, 그리고 함수의 끝을 end로 나타내요. 한 줄짜리 if (cond) 문장 대신, Julia는 if cond; statement; end, cond && statement, !cond || statement 형태의 문장을 허용해요. 마지막 두 문법에서의 할당문은 cond && (x = value)처럼 명시적으로 괄호로 감싸야 해요.
  • Julia에서 <-, <<-, ->는 할당 연산자가 아니에요.
  • Julia의 ->는 익명 함수를 만들어요.
  • Julia의 * 연산자는 R과 달리 행렬 곱을 할 수 있어요. AB가 행렬이면 Julia에서 A * B는 행렬 곱을 뜻하는데, R의 A %*% B와 같아요. R에서는 같은 표기법이 원소별(Hadamard) 곱을 수행해요. 원소별 곱 연산을 얻으려면 Julia에서는 A .* B라고 써야 해요.
  • Julia는 transpose 함수로 행렬 전치를, ' 연산자나 adjoint 함수로 켤레 전치를 수행해요. 따라서 Julia의 transpose(A)는 R의 t(A)와 같아요. 게다가 Julia에서 비재귀적 전치는 permutedims 함수로 제공돼요.
  • Julia는 if 문이나 for/while 루프를 쓸 때 괄호를 요구하지 않아요. for (i in c(1, 2, 3)) 대신 for i in [1, 2, 3]을, if (i == 1) 대신 if i == 1을 쓰세요.
  • Julia는 숫자 0과 1을 불리언으로 취급하지 않아요. Julia에서는 if (1)이라고 쓸 수 없어요. if 문은 불리언만 받아들이니까요. 대신 if true, if Bool(1), if 1==1이라고 쓸 수 있어요.
  • Julia는 nrowncol을 제공하지 않아요. 대신 nrow(M)에는 size(M, 1), ncol(M)에는 size(M, 2)를 쓰세요.
  • Julia는 스칼라, 벡터, 행렬을 신중하게 구분해요. R에서 1c(1)은 같지만, Julia에서는 서로 바꿔 쓸 수 없어요.
  • Julia의 diagdiagm은 R의 것과 다르지 않아요.
  • Julia는 할당 연산의 왼쪽에 함수 호출 결과를 할당할 수 없어요. diag(M) = fill(1, n)이라고 쓸 수 없죠.
  • Julia는 주 네임스페이스에 함수를 채우는 것을 권장하지 않아요. Julia의 통계 기능 대부분은 JuliaStats 조직 아래의 패키지에서 찾을 수 있어요. 예를 들어 확률 분포와 관련된 함수는 Distributions 패키지가 제공해요.
  • DataFrames 패키지가 데이터 프레임을 제공해요.
  • 일반화 선형 모델은 GLM 패키지가 제공해요.
  • Julia는 튜플과 진짜 해시 테이블을 제공하지만, R 스타일의 리스트는 제공하지 않아요. 여러 항목을 반환할 때는 보통 튜플이나 named tuple을 써야 해요. list(a = 1, b = 2) 대신 (1, 2)(a=1, b=2)를 쓰세요.
  • Julia는 사용자가 자기만의 타입을 쓰도록 권장하는데, 그런 타입은 R의 S3·S4 객체보다 쓰기 쉬워요. Julia의 다중 디스패치 시스템 덕분에 table(x::TypeA)table(x::TypeB)는 R의 table.TypeA(x)table.TypeB(x)처럼 동작해요.
  • Julia에서 값은 할당하거나 함수에 전달할 때 복사되지 않아요. 함수가 배열을 수정하면 변경이 호출자에게 보여요. 이는 R과 아주 달라서, 새 함수가 큰 데이터 구조를 훨씬 효율적으로 다룰 수 있게 해줘요.
  • Julia에서 벡터와 행렬은 R의 c, rbind, cbind가 아니라 hcat, vcat, hvcat으로 연결해요.
  • Julia에서 a:b 같은 범위는 R에서처럼 벡터의 약칭이 아니라, 순회에 쓰이는 특수화된 AbstractRange 객체예요. 범위를 벡터로 변환하려면 collect(a:b)를 쓰세요.
  • : 연산자는 R과 Julia에서 우선순위가 달라요. 특히 Julia에서는 산술 연산자가 : 연산자보다 우선순위가 높은 반면, R에서는 그 반대예요. 예를 들어 Julia의 1:n-1은 R의 1:(n-1)과 같아요.
  • Julia의 maxmin은 각각 R의 pmaxpmin에 해당하지만, 두 인자 모두 차원이 같아야 해요. maximumminimum이 R에서 maxmin을 대체하지만, 중요한 차이점이 있어요.
  • Julia의 sum, prod, maximum, minimum은 R의 상대편과 달라요. 이 모두는 연산이 수행될 차원을 나타내는 선택적 키워드 인자 dims를 받아요. 예를 들어 Julia에서 A = [1 2; 3 4]이고 R에서 같은 행렬을 B <- rbind(c(1,2),c(3,4))라고 해 보죠. sum(A)sum(B)와 같은 결과를 주지만, sum(A, dims=1)은 각 열의 합을 담은 행 벡터이고 sum(A, dims=2)는 각 행의 합을 담은 열 벡터예요. 이는 R에서 colSums(B)rowSums(B)라는 별개의 함수가 이 기능을 제공하는 것과 대조적이에요. dims 키워드 인자가 벡터이면 합을 수행할 모든 차원을 지정하는데, 합산되는 배열의 차원은 유지돼요. 예를 들어 sum(A, dims=(1,2)) == hcat(10)이에요. 두 번째 인자에 관한 오류 검사는 없다는 점을 알아두세요.
  • Julia에는 인자를 변형(mutate)할 수 있는 함수가 여럿 있어요. 예를 들어 sortsort! 둘 다 있어요.
  • R에서 성능은 벡터화를 요구해요. Julia에서는 거의 반대예요. 최고 성능의 코드는 종종 벡터화하지 않은 루프로 얻어지죠.
  • Julia는 열심(eager) 평가를 하고 R 스타일의 지연(lazy) 평가를 지원하지 않아요. 대부분의 사용자에게 이는 인용되지 않은 표현식이나 열 이름이 아주 적다는 뜻이에요.
  • Julia는 NULL 타입을 지원하지 않아요. 가장 가까운 동등물은 nothing인데, 리스트처럼이 아니라 스칼라 값처럼 동작해요. is.null(x) 대신 x === nothing을 쓰세요.
  • Julia에서 누락 값은 NA가 아니라 missing 객체로 표현돼요. is.na(x) 대신 ismissing(x)(벡터에 원소별로 적용하려면 ismissing.(x))를 쓰세요. na.rm=TRUE 대신 일반적으로 skipmissing 함수를 써요(다만 특정 경우에는 함수들이 skipmissing 인자를 받을 때도 있어요).
  • Julia에는 R의 assign이나 get에 해당하는 것이 없어요.
  • Julia에서 return은 괄호를 요구하지 않아요.
  • R에서 원하지 않는 값을 제거하는 관용적 방법은 x[x>3] 표현식이나 원위치에서 x를 수정하는 x = x[x>3] 문장처럼 논리 인덱싱을 쓰는 거예요. 반대로 Julia는 고차 함수 filterfilter!를 제공해서, 사용자들이 대응되는 직역 x[x.>3]x = x[x.>3]의 대안으로 filter(z->z>3, x)filter!(z->z>3, x)를 쓸 수 있게 해줘요. filter!를 쓰면 임시 배열 사용이 줄어요.

Python과의 주목할 만한 차이점

  • Julia의 for, if, while 등의 블록은 end 키워드로 끝나요. 들여쓰기 수준은 Python에서처럼 중요하지 않아요. Python과 달리 Julia에는 pass 키워드가 없어요.
  • 문자열은 Julia에서 큰따옴표("text")로 표시돼요(여러 줄 문자열은 큰따옴표 3개). 반면 Python에서는 작은따옴표('text')나 큰따옴표("text") 어느 쪽으로도 표시할 수 있어요. Julia에서 작은따옴표는 문자에 쓰여요('c').
  • 문자열 연결은 Python의 +가 아니라 Julia에서는 *로 해요. 비슷하게 문자열 반복은 *가 아니라 ^로 해요. Python처럼 문자열 리터럴의 암묵적 연결(예: 'ab' 'cd' == 'abcd')은 Julia에서 하지 않아요.
  • Python 리스트들(유연하지만 느린)은 Julia의 Vector{Any} 타입, 더 일반적으로는 T가 어떤 비구체적(non-concrete) 요소 타입인 Vector{T}에 대응돼요. NumPy 배열처럼 요소를 원위치에 저장하는 "빠른" 배열(즉 dtype이 np.float64, [('f1', np.uint64), ('f2', np.int32)] 등)은 T가 구체적이고 불변인 요소 타입인 Array{T}로 나타낼 수 있어요. 여기에는 Float64, Int32, Int64 같은 내장 타입뿐 아니라 Tuple{UInt64,Float64} 같은 더 복잡한 타입과 많은 사용자 정의 타입도 포함돼요.
  • Julia에서 배열, 문자열 등의 인덱싱은 0이 아니라 1부터 시작돼요.
  • Julia의 슬라이스 인덱싱은 Python과 달리 마지막 요소를 포함해요. Julia의 a[2:3]은 Python의 a[1:3]이에요.
  • Python과 달리 Julia는 임의의 인덱스를 가진 AbstractArray를 허용해요. Python의 음수 인덱싱의 특수 해석 a[-1]a[-2]는 Julia에서는 a[end]a[end-1]로 써야 해요.
  • Julia는 마지막 요소까지 인덱싱하려면 end가 필요해요. Julia의 x[2:end]는 Python의 x[1:]과 같아요.
  • Julia에서 어떤 객체 앞의 :Symbol을 만들거나 표현식을 인용해요. 그래서 x[:5]x[5]와 같아요. 배열의 처음 n개 요소를 얻으려면 범위 인덱싱을 쓰세요.
  • Julia의 범위 인덱싱은 x[start:step:stop] 형식인 반면, Python의 형식은 x[start:(stop+1):step]이에요. 따라서 Python의 x[0:10:2]는 Julia의 x[1:2:10]과 같아요. 비슷하게, 뒤집힌 배열을 가리키는 Python의 x[::-1]은 Julia의 x[end:-1:1]과 같아요.
  • Julia에서 범위는 배열 인덱싱에서 쓰는 것과 같은 문법인 start:step:stop으로 독립적으로 구성할 수 있어요. range 함수도 지원돼요.
  • Julia에서 X[[1,2], [1,3]]처럼 배열로 행렬을 인덱싱하면, 첫·두 번째 행과 첫·세 번째 열의 교집합을 담은 부분 행렬을 가리켜요. Python에서 X[[1,2], [1,3]]은 행렬의 [1,1][2,3] 셀의 값을 담은 벡터를 가리켜요. Julia의 X[[1,2], [1,3]]은 Python의 X[np.ix_([0,1],[0,2])]과 같아요. Python의 X[[0,1], [0,2]]는 Julia의 X[[CartesianIndex(1,1), CartesianIndex(2,3)]]과 같아요.
  • Julia에는 줄 연속(line continuation) 문법이 없어요. 줄 끝에서 입력이 지금까지 완전한 표현식이면 완료된 것으로 간주하고, 그렇지 않으면 입력이 계속돼요. 표현식이 계속되도록 강제하는 한 가지 방법은 괄호로 감싸는 거예요.
  • Julia 배열은 열 우선(Fortran 순서)인 반면 NumPy 배열은 기본적으로 행 우선(C 순서)이에요. 배열을 순회할 때 최적 성능을 얻으려면 NumPy에 비해 Julia에서 루프의 순서를 뒤집어야 해요(Performance Tips의 해당 절 참조).
  • Julia의 갱신 연산자(예: +=, -=, ...)는 원위치가 아닌 반면 NumPy의 것은 원위치예요. 즉 A = [1, 1]; B = A; B += [3, 3]A의 값을 바꾸지 않아요. 오히려 B라는 이름을 우변 결과인 B = B + 3(새 배열)에 다시 바인딩해요. 원위치 연산에는 B .+= 3(점 연산자 참조), 명시적 루프, 또는 InplaceOps.jl을 쓰세요.
  • Julia는 함수 인자의 기본값을 메서드가 호출될 때마다 평가해요. Python에서는 기본값이 함수 정의 시 한 번만 평가되는 것과 달라요. 예를 들어 f(x=rand()) = x 함수는 인자 없이 호출될 때마다 새 난수를 반환해요. 반면 g(x=[1,2]) = push!(x,3) 함수는 g()로 호출될 때마다 [1,2,3]을 반환해요.
  • Julia에서 키워드 인자는 키워드로 전달해야 해요. Python에서는 보통 위치로 전달할 수 있는 것과 달라요. 키워드 인자를 위치로 전달하려 하면 메서드 시그니처가 바뀌어 MethodError가 나거나 잘못된 메서드를 호출하게 돼요.
  • Julia에서 %는 나머지(remainder) 연산자인 반면 Python에서는 modulus예요.
  • Julia에서 흔히 쓰는 Int 타입은 기계 정수 타입(Int32Int64)에 해당해요. Python에서 int가 임의 길이 정수인 것과 달라요. 이는 Julia에서 Int 타입이 넘쳐서 2^64 == 0이 될 수 있다는 뜻이에요. 더 큰 값이 필요하면 Int128, BigInt 같은 다른 적절한 타입이나 Float64 같은 부동소수점 타입을 쓰세요.
  • 허수 단위 sqrt(-1)은 Julia에서 im으로 표현돼요. Python처럼 j가 아니에요.
  • Julia에서 지수 연산자는 ^이지 Python처럼 **가 아니에요.
  • Julia는 널 값을 나타내는 데 Nothing 타입의 nothing을 쓰는데, Python은 NoneType 타입의 None을 써요.
  • Julia에서 행렬 타입에 대한 표준 연산자는 행렬 연산인 반면, Python에서는 표준 연산자가 원소별 연산이에요. AB가 둘 다 행렬일 때, Julia의 A * B는 Python처럼 원소별 곱이 아니라 행렬 곱을 수행해요. Julia의 A * B는 Python의 A @ B와 같고, Python의 A * B는 Julia의 A .* B와 같아요.
  • Julia에서 스칼라 값을 반환하는 함수를 배열에 원소별로 적용하고 싶다면 브로드캐스팅 문법 f.(A)f(A) 대신 쓰세요. 어떤 경우에는 두 연산이 모두 정의되지만 뜻이 달라요. numpy.exp(A)는 원소별로 적용하고 scipy.linalg.expm(A)는 행렬 지수이지만, Julia에서 exp.(A)는 원소별로 적용하고 exp(A)는 행렬 지수예요.
  • Julia의 수반 연산자 '는 벡터의 수반(행 벡터의 지연 표현)을 반환하지만, Python에서 벡터에 대한 전치 연산자 .T는 원래 벡터(비연산)를 반환해요.
  • Julia에서 함수는 여러 개의 구체적 구현(메서드라고 부름)을 담을 수 있는데, 이들은 호출의 모든 인자 타입에 기반한 다중 디스패치로 선택돼요. 반면 Python 함수는 단일 구현이고 다형성이 없어요(Python 메서드 호출은 다른 문법을 쓰고 메서드의 수신자에 디스패치하는 것과 대조적).
  • Julia에는 클래스가 없어요. 대신 데이터만 담고 메서드는 없는 구조체(가변 또는 불변)가 있어요.
  • Python에서 클래스 인스턴스의 메서드 호출(x = MyClass(*args); x.f(y))은 Julia의 함수 호출에 해당해요. 예: x = MyType(args...); f(x, y). 일반적으로 다중 디스패치는 Python 클래스 시스템보다 더 유연하고 강력해요.
  • Julia 구조체는 추상 슈퍼타입을 정확히 하나 가질 수 있지만, Python 클래스는 (추상이든 구체든) 하나 이상의 슈퍼클래스를 상속할 수 있어요.
  • Julia의 논리적 프로그램 구조(패키지와 모듈)는 파일 구조와 독립적이지만, Python 코드 구조는 디렉터리(패키지)와 파일(모듈)로 정의돼요.
  • Julia에서는 큰 모듈의 텍스트를 파일별로 새 모듈을 도입하지 않고 여러 파일로 쪼개는 게 관용적이에요. 코드는 메인 파일의 단일 모듈 안에서 include로 다시 조립돼요. Python의 동등물(exec)은 이 용도로는 일반적이지 않지만(이미 있던 정의를 조용히 덮어쓸 거예요), Julia 프로그램은 using이나 import로 모듈 수준에서 단위로 정의되며, include처럼 처음 필요할 때 한 번만 실행돼요. 그런 모듈 안에서 그 모듈을 이루는 개별 파일들은 의도한 순서대로 한 번씩 나열해 include로 불러와져요.
  • Julia의 삼항 연산자 x > 0 ? 1 : -1은 Python의 조건식 1 if x > 0 else -1에 해당해요.
  • Julia에서 @ 기호는 매크로를 가리키지만, Python에서는 데코레이터를 가리켜요.
  • Julia의 예외 처리는 try — catch — finally로 하지만, Python은 try — except — finally로 해요. Python과 달리 Julia에서는 예외 처리를 일반 워크플로의 일부로 쓰는 것을 권장하지 않아요(Python과 비교해 Julia는 일반 제어 흐름이 더 빠르지만 예외 포착은 더 느려요).
  • Julia의 루프는 빠르므로 성능 때문에 "벡터화된" 코드를 쓸 필요가 없어요.
  • 특히 빡빡한 루프에서는 Julia의 비상수 전역 변수에 주의하세요. Julia에서는 (Python과 달리) 금속에 가까운 코드를 쓸 수 있으므로 전역의 영향이 급격할 수 있어요(Performance Tips 참조).
  • Julia에서 반올림과 절단(truncation)은 명시적이에요. Python의 int(3.7)floor(Int, 3.7)이나 Int(floor(3.7))이어야 하고 round(Int, 3.7)과 구별돼요. floor(x)round(x) 자체는 항상 Int를 반환하는 것이 아니라 x와 같은 타입의 정수 값을 반환해요.
  • Julia에서 파싱은 명시적이에요. Python의 float("3.7")은 Julia에서 parse(Float64, "3.7")이에요.
  • Python에서는 대부분의 값을 논리 맥락에서 쓸 수 있어요(예: if "a":는 다음 블록이 실행됨을, if "":는 실행되지 않음을 의미). Julia에서는 Bool로의 명시적 변환이 필요해요(예: if "a"는 예외를 던져요). Julia에서 비어 있지 않은 문자열을 테스트하려면 명시적으로 if !isempty("")라고 써야 해요. 놀랍게도 Python에서 if "False"bool("False")는 둘 다 True로 평가돼요("False"는 비어 있지 않은 문자열이니까). Julia에서 parse(Bool, "false")false를 반환해요.
  • Julia에서는 루프, try — catch — finally를 포함한 대부분의 코드 블록이 새로운 로컬 스코프를 도입해요. 컴프리헨션(리스트, 제너레이터 등)은 Python과 Julia 둘 다에서 새 로컬 스코프를 도입하지만, if 블록은 두 언어에서 모두 새 로컬 스코프를 도입하지 않는다는 점에 주의하세요.

C/C++과의 주목할 만한 차이점

  • Julia 배열은 대괄호로 인덱싱하고, A[i,j]처럼 둘 이상의 차원을 가질 수 있어요. 이 문법은 C/C++에서처럼 포인터나 주소에 대한 참조의 문법적 설탕이 아니에요. 배열 구성에 관한 매뉴얼 항목을 보세요.
  • Julia에서 배열, 문자열 등의 인덱싱은 0이 아니라 1부터 시작돼요.
  • Julia 배열은 다른 변수에 할당할 때 복사되지 않아요. A = B 뒤에 B의 요소를 바꾸면 A도 함께 바뀌어요. += 같은 갱신 연산자는 원위치에서 동작하지 않고, 좌변을 우변 표현식의 결과에 다시 바인딩하는 A = A + B와 같아요.
  • Julia 배열은 열 우선(Fortran 순서)인 반면 C/C++ 배열은 기본적으로 행 우선이에요. 배열을 순회할 때 최적 성능을 얻으려면 C/C++에 비해 Julia에서 루프의 순서를 뒤집어야 해요(Performance Tips의 해당 절 참조).
  • Julia 값은 할당하거나 함수에 전달할 때 복사되지 않아요. 함수가 배열을 수정하면 변경이 호출자에게 보여요.
  • Julia에서는 C/C++와 달리 공백이 중요하므로, Julia 프로그램에서 공백을 추가/제거할 때 주의해야 해요.
  • Julia에서 소수점이 없는 리터럴 숫자(예: 42)는 Int 타입의 부호 있는 정수를 만들지만, 기계 워드 크기에 맞지 않게 너무 큰 리터럴은 Int64(IntInt32라면), Int128, 또는 임의로 큰 BigInt 타입 같은 더 큰 크기 타입으로 자동 승격돼요. 부호 없음 및/또는 부호 있음 vs. 부호 없음을 나타내는 L, LL, U, UL, ULL 같은 숫자 리터럴 접미사는 없어요. 십진 리터럴은 항상 부호 있고, (C/C++처럼 0x로 시작하는) 16진 리터럴은 128비트를 넘게 인코딩하지 않는 한 부호 없어요(그 경우 BigInt 타입). 16진 리터럴은 또한 C/C++/Java와 달리(그리고 Julia의 십진 리터럴과 달리) 선행 0을 포함한 리터럴의 길이에 기반한 타입을 가져요. 예를 들어 0x00x00UInt8 타입이고, 0x0000x0000UInt16, 58개 16진 자릿수의 리터럴은 UInt32, 916개는 UInt64, 17~32개는 UInt128, 32개를 넘으면 BigInt 타입이에요. 16진 마스크를 정의할 때는 이를 고려해야 해요. 예를 들어 ~0xf == 0xf0~0x000f == 0xfff0과 아주 달라요. 64비트 Float64와 32비트 Float32 비트 리터럴은 각각 1.01.0f0으로 표현돼요. 부동소수점 리터럴은 정확히 표현할 수 없으면 반올림돼요(BigFloat 타입으로 승격되지 않아요). 부동소수점 리터럴은 C/C++에 더 가까운 동작이에요. 8진(0o 접두사)과 2진(0b 접두사) 리터럴도 부호 없는(128비트를 넘으면 BigInt) 것으로 취급돼요.
  • Julia에서 나눗셈 연산자 /는 두 피연산자가 모두 정수 타입이어도 부동소수점 숫자를 반환해요. 정수 나눗셈을 수행하려면 div÷를 쓰세요.
  • Julia에서 부동소수점 타입으로 배열을 인덱싱하는 것은 일반적으로 오류예요. C 표현식 a[i / 2]의 Julia 동등물은 a[i ÷ 2 + 1]인데, 여기서 i는 정수 타입이에요.
  • 문자열 리터럴은 """"로 구분할 수 있는데, """로 구분된 리터럴은 "\"처럼 따옴표 없이 " 문자를 담을 수 있어요. 문자열 리터럴에는 $variablename이나 $(expression)으로 표시된 다른 변수·표현식의 값을 보간할 수 있는데, 이는 함수의 맥락에서 변수 이름이나 표현식을 평가해요.
  • //는 유리수(Rational)를 나타내지, 한 줄 주석이 아니에요(한 줄 주석은 Julia에서 #).
  • #=는 여러 줄 주석의 시작을, =#는 끝을 나타내요.
  • Julia 함수는 마지막 표현식(들)이나 return 키워드에서 값을 반환해요. 여러 값을 함수에서 반환해 (a, b) = myfunction()이나 a, b = myfunction()처럼 튜플로 할당할 수 있는데, C/C++에서처럼 값에 포인터를 전달해야 할 필요가 없어요(즉 a = myfunction(&b)).
  • Julia는 문장을 끝내기 위해 세미콜론을 요구하지 않아요. 표현식의 결과는 자동으로 출력되지 않고(대화형 프롬프트, 즉 REPL을 제외하고), 코드 줄을 세미콜론으로 끝낼 필요도 없어요. 특정 출력을 내려면 println이나 @printf를 쓸 수 있어요. REPL에서 ;는 출력을 숨기는 데 쓸 수 있어요. ;[ ] 안에서 다른 뜻을 가지니 주의해야 해요. ;는 한 줄에 표현식을 구분하는 데 쓸 수 있지만 많은 경우 엄격히 필요하지 않고, 가독성을 돕는 정도예요.
  • Julia에서 연산자 (xor)는 비트 XOR 연산을 수행하는데, C/C++에서는 ^예요. 또한 비트 연산자는 C/C++와 같은 우선순위가 아니므로 괄호가 필요할 수 있어요.
  • Julia의 ^는 지수(pow)이지 C/C++처럼 비트 XOR이 아니에요(Julia에서는 또는 xor을 쓰세요).
  • Julia에는 두 개의 오른쪽 시프트 연산자 >>>>>가 있어요. >>는 산술 시프트를, >>>는 항상 논리 시프트를 수행해요. C/C++에서 시프트되는 값의 타입에 >>의 의미가 달라지는 것과 달라요.
  • Julia의 ->는 익명 함수를 만들지, 포인터로 멤버에 접근하지 않아요.
  • Julia는 if 문이나 for/while 루프를 쓸 때 괄호를 요구하지 않아요. for (int i=1; i <= 3; i++) 대신 for i in [1, 2, 3]을, if (i == 1) 대신 if i == 1을 쓰세요.
  • Julia는 숫자 0과 1을 불리언으로 취급하지 않아요. Julia에서는 if (1)이라고 쓸 수 없어요. if 문은 불리언만 받아들이니까요. 대신 if true, if Bool(1), if 1==1이라고 쓸 수 있어요.
  • Julia는 if 같은 조건 블록, while/for 같은 루프 블록, 그리고 함수의 끝을 end로 나타내요. 한 줄짜리 if (cond) 문장 대신, Julia는 if cond; statement; end, cond && statement, !cond || statement 형태의 문장을 허용해요. 마지막 두 문법에서의 할당문은 cond && (x = value)처럼 명시적으로 괄호로 감싸야 해요.
  • Julia에는 줄 연속(line continuation) 문법이 없어요. 줄 끝에서 입력이 지금까지 완전한 표현식이면 완료된 것으로 간주하고, 그렇지 않으면 입력이 계속돼요. 표현식이 계속되도록 강제하는 한 가지 방법은 괄호로 감싸는 거예요.
  • Julia 매크로는 프로그램의 텍스트가 아니라 파싱된 표현식 위에서 동작해서, Julia 코드의 정교한 변형을 수행할 수 있게 해줘요. 매크로는 C/C++ 전처리 매크로와 달리 텍스트 대체가 아니에요.
  • Julia에는 열거 타입이 있는데, @enum(name, value1, value2, ...) 매크로로 표현해요. 예: @enum(Fruit, banana=1, apple, pear).
  • 관례상 인자를 변형하는 함수는 이름 끝에 !를 가져요. 예를 들어 push!.
  • C++에서는 기본적으로 정적 디스패치가 있어서, 동적 디스패치를 얻으려면 함수를 virtual로 표시해야 해요. 반면 Julia에서는 모든 함수가 기본적으로 동적이에요.

Julia ⇔ C/C++: 네임스페이스 (Namespaces)

  • C/C++ 네임스페이스는 대략 Julia 모듈에 해당해요.
  • Julia에는 private 전역이나 필드가 없어요. 모든 것이 완전히 정규화된 경로(원하면 상대 경로)로 공개적으로 접근 가능해요.
  • using MyNamespace::myfun(C++)은 대략 import MyModule: myfun(Julia)에 해당해요.
  • using namespace MyNamespace(C++)은 대략 using MyModule(Julia)에 해당해요. Julia에서는 내보낸(export) 심볼만 호출 모듈에서 사용 가능해져요.
  • C++에서는 포함된(공개) 헤더 파일에서 찾아진 요소만 사용 가능해져요.
  • 주의: import/using 키워드(Julia)는 모듈도 불러와요(아래 참조).
  • 주의: import/using(Julia)은 전역 스코프 수준(모듈)에서만 동작해요. C++에서 using namespace X는 임의의 스코프(예: 함수 스코프)에서 동작해요.

Julia ⇔ C/C++: 모듈 불러오기 (Module loading)

  • C/C++ "라이브러리"를 생각하면, 아마 Julia "패키지"를 찾고 있는 거예요. 주의: C/C++ 라이브러리는 종종 여러 "소프트웨어 모듈"을 담는 반면, Julia "패키지"는 보통 하나를 담아요.
  • 상기: Julia 모듈은 전역 스코프이지(꼭 "소프트웨어 모듈"이 아니에요) 않아요.
  • build/make 스크립트 대신, Julia는 "프로젝트 환경(Project Environments)"(때로는 "Project"나 "Environment"라 부름)을 써요. 빌드 스크립트는 더 복잡한 애플리케이션(C/C++ 실행 파일을 컴파일하거나 다운로드해야 하는 것들)에서만 필요해요.
  • Julia에서 애플리케이션이나 프로젝트를 개발하려면, 그 루트 디렉터리를 "프로젝트 환경"으로 초기화하고 애플리케이션 특화 코드/패키지를 거기에 둘 수 있어요. 이는 프로젝트 의존성에 대한 좋은 제어와 미래의 재현성을 제공해요.
  • 사용 가능한 패키지는 Pkg.add() 함수나 Pkg REPL 모드로 "프로젝트 환경"에 추가돼요. (하지만 이건 그 패키지를 불러오지는 않아요.)
  • "프로젝트 환경"의 사용 가능한 패키지(직접 의존성) 목록은 그 Project.toml 파일에 저장돼요.
  • "프로젝트 환경"의 전체 의존성 정보는 Pkg.resolve()로 자동 생성돼 그 Manifest.toml 파일에 저장돼요.
  • "프로젝트 환경"에서 사용 가능한 패키지("소프트웨어 모듈")는 importusing으로 불러와져요. C/C++에서는 #include <moduleheader>로 객체/함수 선언을 얻고, 실행 파일을 빌드할 때 라이브러리를 링크해요.
  • Julia에서는 using/import를 다시 호출해도 기존 모듈을 스코프로 가져올 뿐 다시 불러오지는 않아요(C/C++에 비표준 #pragma once를 추가하는 것과 비슷).
  • 디렉터리 기반 패키지 저장소(Julia)는 Base.LOAD_PATH 배열에 저장소 경로를 추가해 사용 가능하게 만들 수 있어요. 디렉터리 기반 저장소의 패키지는 importusing으로 불러오기 전에 Pkg.add() 도구가 필요하지 않아요. 그냥 프로젝트에서 사용 가능할 뿐이에요.
  • 디렉터리 기반 패키지 저장소는 "소프트웨어 모듈"의 로컬 라이브러리를 개발하는 가장 빠른 해결책이에요.

Julia ⇔ C/C++: 모듈 조립 (Assembling modules)

  • C/C++에서 .c/.cpp 파일은 build/make 스크립트로 컴파일되어 라이브러리에 추가돼요. Julia에서 import [PkgName]/using [PkgName] 문장은 패키지의 [PkgName]/src/ 하위 디렉터리에 있는 [PkgName].jl을 불러와요.
  • 차례로, [PkgName].jl은 보통 include "[someotherfile].jl" 호출로 관련 소스 파일을 불러와요.
  • include "./path/to/somefile.jl"(Julia)은 #include "./path/to/somefile.jl"(C/C++)과 아주 비슷해요. 하지만 include "..."(Julia)은 헤더 파일을 포함하는 데 쓰이지 않아요(필요하지 않으므로).
  • 다른 "소프트웨어 모듈"에서 코드를 불러오는 데 include "..."(Julia)을 쓰지 마세요(import/using을 쓰세요).
  • include "path/to/some/module.jl"(Julia)은 서로 다른 모듈에서 같은 코드의 여러 버전을 인스턴스화해(같은 이름을 가진 구별되는 타입들을 만들어)요.
  • include "somefile.jl"은 보통 같은 Julia 패키지("소프트웨어 모듈") 안의 여러 파일을 조립하는 데 쓰여요. 따라서 파일이 한 번만 포함되도록 하는 것이 상대적으로 간단해요(#ifdef 혼동 없음).

Julia ⇔ C/C++: 모듈 인터페이스 (Module interface)

  • C++는 "public" .h/.hpp 파일로 인터페이스를 노출하는 반면, Julia 모듈은 사용자용으로 의도된 특정 심볼을 public이나 export로 표시해요. 종종 Julia 모듈은 기존 함수에 새 "메서드"를 만들어 기능을 추가하기만 해요(예: Base.push!).
  • 따라서 Julia 패키지 개발자는 인터페이스 문서화를 위해 헤더 파일에 의존할 수 없어요.
  • Julia 패키지의 인터페이스는 보통 docstring, README.md, 정적 웹 페이지 등으로 설명돼요.
  • 어떤 개발자는 패키지/모듈을 쓰는 데 필요한 모든 심볼을 내보내지 않기로 하면서도, 내보내지 않은 사용자 대면 심볼은 여전히 public으로 표시해야 해요. 사용자들은 그 구성 요소를 함수/구조체 등을 패키지/모듈 이름으로 정규화해 접근하리라 기대할 수 있어요(예: MyModule.run_this_task(...)).

Julia ⇔ C/C++: 빠른 참조 (Quick reference)

소프트웨어 개념 Julia C/C++
이름 없는 스코프 begin ... end { ... }
함수 스코프 function x() ... end int x() { ... }
전역 스코프 module MyMod ... end namespace MyNS { ... }
소프트웨어 모듈 Julia "패키지" .h/.hpp 파일 + 컴파일된 somelib.a
소프트웨어 모듈 조립 SomePkg.jl: ...
import("subfile1.jl")
import("subfile2.jl")
...
$(AR) *.o ⇒ somelib.a
소프트웨어 모듈 불러오기 import SomePkg #include <somelib> + somelib.a 링크
모듈 라이브러리 LOAD_PATH[], *Git 저장소, **커스텀 패키지 레지스트리 더 많은 .h/.hpp 파일 + 더 큰 컴파일된 somebiglib.a
  • * Julia 패키지 관리자는 단일 Git 저장소에서 여러 패키지를 등록하는 것을 지원해요. * 이는 사용자가 관련 패키지들의 라이브러리를 단일 저장소에 두게 해줘요.
  • ** Julia 레지스트리는 주로 패키지의 버전 관리와 배포를 제공하도록 설계됐어요. ** 커스텀 패키지 레지스트리는 일종의 모듈 라이브러리를 만드는 데 쓸 수 있어요.

Common Lisp과의 주목할 만한 차이점

  • Julia는 기본적으로 배열에 1-기반 인덱싱을 쓰고, 임의의 인덱스 오프셋도 다룰 수 있어요.
  • 함수와 변수는 같은 네임스페이스를 공유해요("Lisp-1").
  • Pair 타입은 있지만 COMMON-LISP:CONS로 쓰이도록 의도되지는 않아요. 다양한 순회 가능 컬렉션을 언어의 대부분 부분에서 서로 바꿔 쓸 수 있어요(예: 스플래팅, 튜플 등). 이질 요소의 짧은 컬렉션에는 Common Lisp 리스트에 가장 가까운 것이 튜플이에요. alist 대신 NamedTuple을 쓰세요. 동질 타입의 더 큰 컬렉션에는 Array와 Dict를 써야 해요.
  • 프로토타이핑의 전형적인 Julia 워크플로도 Revise.jl 패키지로 구현된 이미지의 연속 조작을 사용해요.
  • 성능을 위해 Julia는 연산이 타입 안정성을 갖는 것을 선호해요. Common Lisp가 밑바탕의 기계 연산에서 추상화하는 반면, Julia는 그들에 더 가깝게 붙어요. 예를 들어 /를 사용한 정수 나눗셈은 계산이 정확해도 항상 부동소수점 결과를 반환해요. //는 항상 유리수 결과를 반환하고, ÷는 항상 (절단된) 정수 결과를 반환해요.
  • Bignum은 지원되지만 변환은 자동이 아니고, 일반 정수는 넘칠(overflow) 수 있어요.
  • 복소수는 지원되지만, 복소수 결과를 얻으려면 복소수 입력이 필요해요.
  • 서로 다른 성분 타입을 가진 여러 ComplexRational 타입이 있어요.
  • 모듈(네임스페이스)은 계층적일 수 있어요. importusing은 이중 역할을 해요. 코드를 불러오고 네임스페이스에서 사용 가능하게 만들죠. 모듈 이름만 import하는 것도 가능해요(대략 ASDF:LOAD-OP에 해당). 슬롯 이름을 따로 내보낼 필요는 없어요. 전역 변수는 모듈 밖에서 할당할 수 없어요(eval(mod, :(var = val)) 탈출구 제외).
  • 매크로는 @로 시작하고, Common Lisp만큼 언어에 매끄럽게 통합되지 않아요. 결과적으로 매크로 사용이 Common Lisp만큼 널리 퍼져 있지 않아요. 매크로 위생(humidity)의 한 형태는 언어가 지원해요. 표면 문법이 다르므로 COMMON-LISP:&BODY에 해당하는 것이 없어요.
  • 모든 함수는 제네릭이고 다중 디스패치를 사용해요. 인자 목록이 같은 템플릿을 따를 필요가 없어서 강력한 관용구로 이어져요(do 참조). 선택적 인자와 키워드 인자는 다르게 처리돼요. 메서드 모호성은 Common Lisp Object System처럼 해결되지 않아서, 교집합에 대해 더 구체적인 메서드를 정의해야 해요.
  • 심볼은 어떤 패키지에도 속하지 않고 그 자체로 어떤 값도 담지 않아요. M.var는 모듈 M에서 심볼 var를 평가해요.
  • 함수형 프로그래밍 스타일은 클로저를 포함해 언어가 완전히 지원하지만, 항상 Julia의 관용적 해결책은 아니에요. 포착된 변수를 수정할 때 성능을 위해 어떤 우회가 필요할 수 있어요.

더 알아보기

  • Julia의 자주 묻는 질문 (Frequently Asked Questions)
  • 유니코드 입력 (Unicode Input)
  • MATLAB·R·Python·C/C++에서 온 사용자를 위한 더 많은 팁은 Julia 커뮤니티 문서