Документация Engee

Примечательные отличия от других языков

Хотя пользователям MATLAB синтаксис Julia может показаться знакомым, Julia не является клоном MATLAB. Между ними есть важные синтаксические и функциональные различия. Ниже перечислены некоторые примечательные отличия Julia, которые могут сбить с толку пользователей, привыкших к MATLAB.

Отличия от матлаб

  • Индексы массивов в Julia указываются в квадратных скобках: A[i,j].

A = [1 2 3; 4 5 6; 7 8 9]
i = 2
j = 3
element = A[i, j]
println(element)
6
  • Когда массив в Julia присваивается другой переменной, он не копируется. После присваивания A = B изменение элементов B приведет к их изменению в A.

B = [1, 2, 3]
A = B
B[1] = 10
println(A)
[10, 2, 3]
  • В Julia значения, передаваемые в функцию, не копируются. Если функция изменяет массив, эти изменения будут видны вызывающей стороне.

function modify_array(arr)
    arr[1] = 100
end

A = [1, 2, 3]
modify_array(A)
println(A)
[100, 2, 3]
  • В Julia массивы не увеличиваются автоматически при использовании оператора присваивания. Тогда как в MATLAB с помощью выражения a(4) = 3.2 можно создать массив a = [0 0 0 3.2], а с помощью выражения a(5) = 7 — увеличить его до a = [0 0 0 3.2 7], в Julia соответствующий оператор a[5] = 7 выдает ошибку, если длина a меньше 5 или если имя a впервые используется в этом операторе. В Julia есть функции push! и append!, которые позволяют увеличивать объекты типа Vector гораздо эффективнее, чем a(end+1) = val в MATLAB.

A = [1, 2, 3]
push!(A, 4)
println(A)

B = [5, 6, 7]
append!(B, [8, 9])
println(B)
[1, 2, 3, 4]
[5, 6, 7, 8, 9]
  • Мнимая единица sqrt(-1) представлена в Julia константой im, а не i или j, как в MATLAB.

z = 2 + 3im
println(z)
println("Действительная часть: ", real(z))
println("Мнимая часть: ", imag(z))
2 + 3im
Действительная часть: 2
Мнимая часть: 3
  • В Julia числовые литералы без десятичного разделителя (например, 42) создают целые числа, а не числа с плавающей запятой. В результате некоторые операции могут приводить к ошибке выхода за пределы области, если они ожидают число с плавающей запятой. Например, julia> a = -1; 2^a выдаст такую ошибку, так как результат не целочисленный (подробные сведения см. в разделе FAQ об ошибках выхода за пределы области).

a = 42  # целое число
b = 42.0 # число с плавающей запятой
c = float(42) # явное преобразование к типу Float64
println(a)
println(b)
println(c)
42
42.0
42.0
  • Если в Julia нужно вернуть или присвоить несколько значений, используются кортежи, например (a, b) = (1, 2) или a, b = 1, 2. В Julia нет функции nargout, которая часто применяется в MATLAB для выполнения дополнительных действий в зависимости от количества возвращенных значений. В аналогичных целях можно использовать необязательные и именованные аргументы.

function get_values()
    return 1, 2
end

x, y = get_values()
println(x)
println(y)
1
2
  • В Julia есть истинные одномерные массивы. Векторы столбцов имеют размер N, а не Nx1. Например, выражение rand(N) создает одномерный массив.

v = [1, 2, 3] # одномерный массив
3-element Vector{Int64}:
 1
 2
 3
  • В Julia конструкция [x,y,z] всегда создает массив, содержащий три элемента: x, y и z. — Для конкатенации по первому («вертикальному») измерению вызовите функцию vcat(x,y,z) или используйте в качестве разделителя точку с запятой ([x; y; z]). — Для конкатенации по второму («горизонтальному») измерению вызовите функцию hcat(x,y,z) или используйте в качестве разделителя пробел ([x y z]). — Для создания блочных матриц (с конкатенацией по первым двум измерениям) вызовите функцию hvcat или комбинируйте пробелы и точки с запятой в качестве разделителей ([a b; c d]).

A = [1, 2, 3]
B = [4, 5, 6]
C = [7, 8, 9]

D = vcat(A, B, C) # вертикальная конкатенация
println(D)

E = hcat(A, B, C) # горизонтальная конкатенация
println(E)

F = [A; B; C] # разделитель ";"
println(F)

G = [A B C] # разделитель пробел
println(G)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1 4 7; 2 5 8; 3 6 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1 4 7; 2 5 8; 3 6 9]
  • В Julia конструкции a:b и a:b :c создают объекты AbstractRange. Для создания полного вектора, как в MATLAB, используйте метод collect(a:b). Однако обычно вызывать collect не нужно. Объект AbstractRange в большинстве случаев ведет себя как обычный массив, однако он эффективнее благодаря «ленивому» вычислению значений. Этот шаблон создания специальных объектов вместо полных массивов применяется часто, в том числе с функциями, такими как range, и итераторами, такими как enumerate и zip. Специальные объекты, как правило, можно использовать как обычные массивы.

range = 1:5
vector = collect(range)
println(vector)
[1, 2, 3, 4, 5]
  • Функции в Julia возвращают результат вычисления последнего выражения или выражения с ключевым словом return. Перечислять имена возвращаемых переменных в определении функции не нужно (подробные сведения см. в разделе Ключевое слово return).

function sum(x, y)
    x + y
end

result = sum(2, 3)
println(result)
5
  • В Julia при вызове функции без аргументов необходимо использовать круглые скобки, например rand().

function example()
    println("Функция без аргументов")
end

example()
Функция без аргументов
  • В Julia не рекомендуется ставить точку с запятой в конце выражений. Результаты выражений не выводятся на экран автоматически (кроме как в интерактивной командной строке), поэтому строки кода нет необходимости заканчивать точкой с запятой. Для вывода на экран определенных данных можно использовать функцию println или макрос @printf.

a = 2
b = 3
c = a + b
println(c)
5
  • В Julia, если A и B — массивы, операции логического сравнения, такие как A == B, не возвращают массив логических значений. Вместо этого используйте A .== B или аналогичные выражения для других логических операторов, таких как < и >.

A = [1, 2, 3]
B = [2, 2, 3]
result = A .== B
println(result)
Bool[0, 1, 1]
  • В Julia операторы &, | и ⊻ (xor) выполняются побитово, так же, как, соответственно, and, or и xor в MATLAB. Их приоритет такой же, как у побитовых операторов в Python (но не такой, как в C). Они могут применяться к скалярным значениям или поэлементно к массивам, а также использоваться для объединения логических массивов. Однако обратите внимание на различие в очередности операций: могут потребоваться круглые скобки (например, для выбора элементов A, равных 1 или 2, используйте выражение (A .== 1) .| (A .== 2)).

a = 5
b = 3
result = a & b
println(result)

A = [true, false, true]
B = [false, true, true]
result = A .& B
println(result)
1
Bool[0, 0, 1]
  • В Julia элементы коллекции могут передаваться в функцию как аргументы с помощью оператора расширения (splat-оператора) …​, например xs=[1,2]; f(xs…​).

function sum_elements(x, y, z)
    return x + y + z
end

values = [1, 2, 3]
result = sum_elements(values...)
println(result)
6
  • Функция svd в Julia возвращает сингулярные значения в виде вектора, а не плотной диагональной матрицы.

using LinearAlgebra
A = [1 2; 3 4]
U, S, V = svd(A)
println(S)
[5.464985704219043, 0.3659661906262575]
  • В Julia символ …​ не применяется для продолжения строк кода. Вместо этого неполное выражение автоматически продолжается в следующей строке.

a = 2 +
    3
println(a)
5
  • Структуры (struct) в Julia не поддерживают динамическое добавление полей во время выполнения, в отличие от классов (class) в MATLAB. Вместо этого используйте Dict. Словари в Julia не упорядочиваются.

struct MyStruct1
    field1::Int
end

s = MyStruct1(10)
println(s.field1)

# Используем словарь для добавления поля field2
s_dict = Dict(:field1 => s.field1, :field2 => 20)
println(s_dict[:field2])
10
20
  • В Julia каждый модуль имеет собственную глобальную область или пространство имен, в то время как в MATLAB глобальная область только одна.

module MyModule
    global_var = 10

    function my_function()
        println(global_var)
    end
end

MyModule.my_function()
10
WARNING: replacing module MyModule.
  • В MATLAB идиоматическим способом удаления ненужных значений является использование логического индексирования, как в выражении x(x>3) или в операторе x(x>3) = [] для изменения x на месте. В Julia с этой целью предоставляются функции более высокого порядка filter и filter!, которые позволяют использовать выражения filter(z→z>3, x) и filter!(z→z>3, x) в качестве альтернативы для x[x.>3] и x = x[x.>3]. Функция filter! позволяет реже прибегать к временным массивам.

A = [1, 2, 3, 4, 5]
filtered_A = filter(x -> x > 3, A)
println(filtered_A)

filter!(x -> x > 3, A)
println(A)
[4, 5]
[4, 5]
  • Извлечение (или «разыменование») всех элементов массива ячеек, например vertcat(A\{:}) в MATLAB, записывается в Julia с помощью оператора расширения (splat-оператор), например vcat(A…​).

A = [1, 2]
B = [3, 4]
C = [5, 6]
array_of_arrays = [A, B, C]
concatenated_array = vcat(array_of_arrays...)
println(concatenated_array)
[1, 2, 3, 4, 5, 6]
  • В Julia функция adjoint производит сопряженное транспонирование; в MATLAB adjoint предоставляет присоединенную матрицу (классическое сопряжение), то есть выполняет транспонирование матрицы алгебраических дополнений.

A = [1+2im 3+4im]
adj_A = adjoint(A)
println(adj_A)
Complex{Int64}[1 - 2im; 3 - 4im;;]
  • В Julia выражение (a ^ b ^ c) вычисляется как a^ (b^c), в то время как в MATLAB — как (a ^ b) ^ c.

a = 3
b = 2
c = 3
result = a^(b^c)
println(result)
6561

Отличия от Python

  • В Julia блоки for, if, while и т. д. заканчиваются ключевым словом end, а не отступом, как в Python. Нет ключевого слова pass.

for i in 1:5
    println(i)
end
1
2
3
4
5
  • В Julia строки обозначаются двойными кавычками, используются для многострочных текстов три пары двойных кавычек. Также, для символов используются одинарные кавычки.

str1 = "Hello, World!"
str2 = """Это много-
строчный текст."""
char1 = 'c'
'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase)
  • В Julia для конкатенации строк используется оператор _, а не +, как в Python. Для повтора строк используется оператор ^, а не _.

str1 = "Hello, "
str2 = "World!"
concatenated_str = str1 * str2
repeated_str = str2 ^ 3
println(concatenated_str)
println(repeated_str)
Hello, World!
World!World!World!
  • В Julia списки соответствуют типу Vector{Any} или более общему типу Vector{T}, где T - некоторый тип элементов. Более эффективные массивы, подобные массивам NumPy, могут быть представлены массивами Array{T}, где T - конкретный тип элементов.

list1 = Any[1, "two", 3.0]
list2 = Vector{Int}(undef, 5)
array1 = Array{Int}(undef, 3, 3)
3×3 Matrix{Int64}:
 3762813801648698725  3774918513298847538  7077182949550143800
 3762529018811868721  7364623875528025187      140502613063936
 3991368380935386977  3906925892200719205      140518684094896
  • Индексирование массивов в Julia начинается с единицы, в отличие от Python, где индексация начинается с нуля.

array = [1, 2, 3, 4, 5]
println(array[1])  # Выведет 1
1
  • При индексировании срезов в Julia последний элемент включается, в отличие от Python. a[2:3] в Julia эквивалентно a[1:3] в Python.

array = [1, 2, 3, 4, 5]
println(array[2:3])  # Выведет [2, 3]
[2, 3]
  • В Julia допустимы массивы AbstractArray с произвольными индексами. Отрицательные индексы, имеющие особый смысл в Python, записываются в Julia в виде a[end] и a[end-1].

array = [1, 2, 3, 4, 5]
println(array[end])  # Выведет 5
println(array[end - 1])  # Выведет 4
5
4
  • Для индексирования до последнего элемента в Julia используется ключевое слово end. x[1:] в Python эквивалентно x[2:end] в Julia.

array = [1, 2, 3, 4, 5]
println(array[2:end])  # Выведет [2, 3, 4, 5]
[2, 3, 4, 5]
  • В Julia индексирование диапазона имеет формат x[start:step:stop], а в Python - x[start:(stop+1):step]. Поэтому x[0:10:2] в Python эквивалентно x[1:2:10] в Julia.

array = [1, 2, 3, 4, 5]
println(array[1:2:end])  # Выведет [1, 3, 5]
[1, 3, 5]
  • В Julia отсутствует синтаксис продолжения строк. Если строка содержит полноценное выражение, то она считается завершенной. В противном случае строка продолжается. Для продолжения выражения можно использовать круглые скобки.

a = (
    1 + 2 +
    3
)
println(a)  # Выведет 6
6
  • В Julia массивы развертываются по столбцам, в то время как массивы NumPy развертываются по строкам. При переборе массивов в Julia порядок циклов должен быть обратным по сравнению с NumPy.

A = [1 2; 3 4; 5 6]
for j in 1:size(A, 2), i in 1:size(A, 1)
    println(A[i, j])
end
1
3
5
2
4
6
  • В Julia значения по умолчанию аргументов функции вычисляются при каждом вызове метода, в отличие от Python, где значения вычисляются только при определении функции.

function f(x = rand())
    return x
end;
println(f())  # Выполните ячейку несколько раз
0.975434444871681
  • В Julia именованные аргументы должны передаваться с указанием имен, в отличие от Python.

function foo(a; b = 1, c = 2)
    return a + b + c
end

println(foo(10, b = 3, c = 4))  # Выведет 17, нельзя вызывать как println(foo(10, 3, 4)), в случае с Python
17
  • В Julia тип Int соответствует машинному целочисленному типу (Int32 или Int64), в то время как в Python int может иметь произвольную длину.

x = Int64(2) ^ Int64(64)
println(x)  # Выведет 0
0
  • Мнимая единица в Julia представлена константой im, в то время как в Python используется j.

println(2im)  # Выведет 0 + 2im
0 + 2im
  • В Julia оператор возведения в степень обозначается ^, а не **, как в Python.

println(2 ^ 3)  # Выведет 8
8
  • Пустое значение в Julia обозначается константой nothing типа Nothing, в то время как в Python используется объект None типа NoneType.

x = nothing
println(x)  # Выведет nothing
nothing
  • В Julia оператор * выполняет матричную операцию, в то время как в Python выполняется поэлементное умножение. В Julia для поэлементного умножения используется оператор .*.

A = [1 2;
     3 4]
B = [5 6;
     7 8]
println(A * B)  # Выведет матрицу, [19 22; 43 50]

println(A .* B)  # Выведет матрицу, [5 12; 21 32]
[19 22; 43 50]
[5 12; 21 32]
  • В Julia оператор ' возвращает сопряжение вектора, в то время как оператор транспонирования .T в Python возвращает исходный вектор.

x = [1 2 3]
println(x')  # Выведет [1; 2; 3]
[1; 2; 3;;]
  • В Julia функция может иметь несколько конкретных реализаций (методов), которые выбираются на основе типов аргументов. В Python функция имеет единственную реализацию, и полиморфизм не поддерживается.

function square(x::Number)
    return x * x
end

function square(x::String)
    return string(x, x)
end

println(square(2))  # Выведет 4
println(square("hello"))  # Выведет "hellohello"
4
hellohello
  • В Julia отсутствуют классы, вместо них используются структуры, которые содержат только данные, но не методы.

struct MyStruct
    x::Int
    y::Float64
end

function f(obj::MyStruct, z)
    return obj.x + obj.y + z
end

my_obj = MyStruct(1, 2.5)
println(f(my_obj, 3.5))  # Выведет 7.0
7.0
  • Вызов метода экземпляра класса в Python соответствует вызову функции в Julia.

struct MyType
    x::Int
end

function f(obj::MyType, y)
    return obj.x + y
end

my_obj = MyType(10)
println(f(my_obj, 5))  # Выведет 15
15
  • Структура в Julia может иметь только один абстрактный супертип, в то время как классы Python могут наследоваться от нескольких суперклассов.

abstract type Animal end

struct Dog <: Animal
    name::String
end

struct Cat <: Animal
    name::String
end

dog = Dog("Buddy")
cat = Cat("Whiskers");
  • В Julia тернарный оператор записывается как x > 0 ? 1 : -1, в то время как в Python используется условное выражение 1 if x > 0 else -1.

x = 10
result = x > 0 ? 1 : -1
println(result)  # Выведет 1
1
  • В Julia обработка исключений осуществляется с помощью конструкции try-catch-finally, в отличие от Python, где используется try-except-finally.

try
    # Код, который может вызвать исключение
catch ex
    # Обработка исключения
finally
    # Выполнение кода, который будет выполнен в любом случае
end

Вывод

В данном примере были продемонстрированы основные синтаксические и функциональные различия, отличающие Julia от Matlab и Python.