cv.cm

코딩 · 프로그래밍 언어

초보자를 위한 Julia 기초 튜토리얼

9분 분량

핵심 답변

Julia는 빠른 수치 및 과학 계산을 위해 설계된 고수준 동적 타입 언어입니다. Python이나 MATLAB처럼 읽히지만 각 함수를 처음 호출할 때 네이티브 코드로 컴파일하므로 반복문이 C에 가까운 속도로 실행될 수 있습니다. 핵심 개념은 다중 디스패치로, 함수가 모든 인자의 타입에 따라 메서드를 고릅니다. juliaup으로 설치하고 julia hello.jl로 파일을 실행합니다.

Julia란 무엇이며 누가 쓰는가

Julia는 Jeff Bezanson, Stefan Karpinski, Viral Shah, Alan Edelman이 만들었고 2012년에 공개되었습니다. 2018년에 1.0이 나왔으며 1.x 시리즈는 하위 호환성을 유지합니다. Julia는 느리지만 쉬운 언어로 프로토타입을 만든 뒤 병목 부분을 C나 Fortran으로 다시 작성하는 두 언어 문제를 해결하는 것을 목표로 합니다.

Julia는 과학자, 엔지니어, 데이터 분석가가 시뮬레이션, 미분방정식, 최적화, 통계, 머신러닝, 고성능 컴퓨팅에 사용합니다. 패키지 생태계는 데이터프레임, 플로팅, GPU 컴퓨팅, 자동 미분을 포괄합니다.

Julia 설치와 Hello, world 실행

권장 설치 도구는 juliaup으로, Windows, macOS, Linux에서 Julia 버전을 설치하고 업데이트합니다(julialang.org/downloads 참고). 설치 후 julia는 REPL을 시작하고 julia file.jl은 스크립트를 실행합니다.

# hello.jl
println("Hello, world!")
julia hello.jl
# Hello, world!
julia
# julia> 1 + 2
# 3

REPL에서 ]를 누르면 패키지 모드(예: add DataFrames)로, ?는 이름에 대한 도움말로, ;는 셸로 들어갑니다. 백스페이스를 누르면 일반 프롬프트로 돌아옵니다.

변수와 기본 타입

변수는 선언이 필요 없지만 모든 값에는 구체적인 타입이 있으며 typeof로 확인할 수 있습니다. 원한다면 함수 인자와 구조체 필드에 타입 주석을 붙일 수 있습니다.

타입예시설명
Int644264비트 시스템의 기본 정수. 오버플로는 값이 한 바퀴 돌아옴
Float643.14, 1e-3기본 부동소수점
Rational1//3정확한 분수
String"hello"UTF-8. "$name"으로 보간
Char'a'유니코드 문자 하나
Booltrue, false논리 값
Symbol:name인턴된 이름으로, 키나 옵션으로 자주 사용
Nothingnothing값이 없음을 뜻하며, 결과가 없는 함수가 반환
name = "Ana"
age = 30
println("$name is $age, next year $(age + 1)")
# Ana is 30, next year 31
typeof(age)   # Int64
7 / 2         # 3.5  (always a float)
7 ÷ 2         # 3    (integer division, also div(7, 2))
2^10          # 1024

Julia는 π나 α 같은 유니코드 이름을 받아들입니다. REPL과 대부분의 편집기에서 \pi를 입력하고 Tab을 누르면 π가 됩니다.

제어 흐름

블록은 end 키워드로 끝나며 들여쓰기는 가독성을 위한 것일 뿐입니다. if, for, while은 대부분의 언어와 같게 동작하고, 컴프리헨션은 한 줄로 배열을 만듭니다.

function sign_name(n)
    if n > 0
        "positive"
    elseif n < 0
        "negative"
    else
        "zero"
    end
end
for i in 1:3
    println("$i squared is $(i^2)")
end
squares = [x^2 for x in 1:5 if isodd(x)]   # [1, 9, 25]
x = 5
result = x > 3 ? "big" : "small"           # "big"

1:3은 양 끝을 포함하는 범위입니다. 함수나 if 블록의 마지막 표현식이 그 값이므로 sign_name(-2)는 명시적인 return 없이 "negative"를 반환합니다.

함수

add(x, y) = x + y                 # one-line definition
function greet(name = "world"; punctuation = "!")
    return "Hello, $name$punctuation"
end
greet()                           # "Hello, world!"
greet("Ana", punctuation = ".")   # "Hello, Ana."
square = x -> x^2                 # anonymous function
map(square, [1, 2, 3])            # [1, 4, 9]
v = [3, 1, 2]
sort!(v)                          # v is now [1, 2, 3]

위치 인자는 세미콜론 앞에, 키워드 인자는 뒤에 둡니다. 관례상 이름이 !로 끝나는 함수는 인자 중 하나를 수정하며(sort!, push! 등), sort는 새 배열을 반환합니다.

배열, 튜플, 딕셔너리

구조리터럴설명
Vector[1, 2, 3]1부터 시작, 변경 가능, push!로 늘어남
Matrix[1 2; 3 4]공백은 열을, ;는 행을 구분
Tuple(1, "a")불변, 고정 길이
Named tuple(name = "Ana", age = 30)필드는 .name으로 접근
DictDict("a" => 1)변경 가능한 해시 맵
SetSet([1, 2, 2])고유한 값
v = [10, 20, 30]
v[1]          # 10   (first element)
v[end]        # 30
push!(v, 40)
A = [1 2; 3 4]
A * A         # matrix product: [7 10; 15 22]
A .* A        # element-wise:   [1 4; 9 16]
v .+ 1        # broadcasting:   [11, 21, 31, 41]
sqrt.([1, 4, 9])   # [1.0, 2.0, 3.0]

점 문법을 브로드캐스팅이라 합니다. f.(x)는 모든 요소에 f를 적용하고, .+, .*는 요소별로 동작합니다. Julia는 점이 붙은 연산의 연쇄를 하나의 반복문으로 합칩니다.

대표 기능: 다중 디스패치

Julia 함수는 여러 메서드를 가질 수 있습니다. 함수를 호출하면 Julia가 모든 인자의 타입에 대해 가장 구체적인 메서드를 고릅니다. 이것이 클래스 메서드를 대신합니다. 타입은 데이터를 담고, 동작은 그 타입에 대한 메서드를 정의해서 추가하며, 다른 패키지에서도 할 수 있습니다.

abstract type Shape end
struct Circle <: Shape
    r::Float64
end
struct Rect <: Shape
    w::Float64
    h::Float64
end
area(c::Circle) = π * c.r^2
area(r::Rect) = r.w * r.h
shapes = [Circle(1.0), Rect(2.0, 3.0)]
println(round.(area.(shapes), digits = 2))   # [3.14, 6.0]

Julia는 보이는 인자 타입에 맞게 특수화된 함수 버전을 컴파일하므로, 함수의 첫 호출은 컴파일이 포함되어 느리고 이후 호출은 빠릅니다. 생성 후 필드를 바꿔야 한다면 mutable struct를 사용하세요.

오류 처리

Julia는 예외를 사용합니다. ArgumentError나 DomainError 같은 내장 예외 타입을 던지거나, Exception의 하위 타입인 구조체로 직접 정의하세요. tryparse는 예외를 던지는 대신 nothing을 반환합니다.

function parse_age(s)
    n = tryparse(Int, s)
    n === nothing && throw(ArgumentError("not a number: $s"))
    n < 0 && throw(DomainError(n, "age must not be negative"))
    return n
end
try
    parse_age("abc")
catch e
    if e isa ArgumentError
        println("Error: ", e.msg)
    else
        rethrow()
    end
end
# Error: not a number: abc

작은 완성 프로그램: 단어 빈도

text = "the cat and the hat and the bat"
counts = Dict{String,Int}()
for w in split(text)
    counts[w] = get(counts, w, 0) + 1
end
top = sort(collect(counts), by = last, rev = true)
for (word, n) in first(top, 2)
    println(word, " ", n)
end
# the 3
# and 2

split은 문자열을 공백 기준으로 나누고, get은 새 단어에 기본값 0을 반환하며, collect는 딕셔너리를 word => count 쌍의 벡터로 바꾸고, by = last는 횟수로 정렬합니다.

다음 단계

  • docs.julialang.org의 공식 Julia 매뉴얼. Getting Started부터 시작하세요.
  • julialang.org의 학습 페이지에는 튜토리얼, 책, 강의가 정리되어 있습니다.
  • 코드를 최적화하기 전에 매뉴얼의 Performance Tips 장을 읽어 보세요.
  • DataFrames.jl, Plots.jl, DifferentialEquations.jl 같은 패키지를 살펴보세요.

자주 묻는 질문

Julia가 Python보다 빠른가요?

반복문과 수치 연산이 많은 코드에서는 Julia가 네이티브 코드로 컴파일되므로 순수 Python보다 보통 훨씬 빠릅니다. NumPy 같은 라이브러리를 쓰는 Python도 벡터화 연산에서는 빠를 수 있으므로 차이는 작업에 따라 다릅니다.

Julia는 왜 1부터 세나요?

Julia는 배열의 첫 요소가 인덱스 1인 수학과 MATLAB, R, Fortran 같은 언어의 관례를 따릅니다.

Julia 코드의 첫 실행이 느린 이유는 무엇인가요?

Julia는 각 함수를 처음 호출할 때 인자 타입에 맞게 컴파일합니다. 이후 호출은 컴파일된 코드를 재사용하므로, 워밍업 호출 이후나 벤치마크 패키지로 성능을 측정하세요.

Julia는 무료인가요?

네. Julia는 MIT 라이선스의 오픈 소스이며 거의 모든 패키지도 오픈 소스입니다.

코딩 튜토리얼 전체