Примечательные отличия от других языков
Примечательные отличия от MATLAB
Хотя пользователям MATLAB синтаксис Julia может показаться знакомым, Julia не является клоном MATLAB. Между ними есть важные синтаксические и функциональные различия. Ниже перечислены некоторые примечательные отличия Julia, которые могут сбить с толку пользователей, привыкших к MATLAB.
-
Индексы массивов в 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) = 7
— увеличить его доa = [0 0 0 3.2 7]
, в Julia соответствующий операторa[5] = 7
выдает ошибку, если длинаa
меньше 5 или если имяa
впервые используется в этом операторе. В Julia есть функцииpush!
иappend!
, которые позволяют увеличивать объектыVector
гораздо эффективнее, чемa(end+1) = val
в MATLAB. -
Мнимая единица
sqrt(-1)
представлена в Julia константойim
, а неi
илиj
, как в MATLAB. -
В Julia числовые литералы без десятичного разделителя (например,
42
) создают целые числа, а не числа с плавающей запятой. В результате некоторые операции могут приводить к ошибке выхода за пределы области, если они ожидают число с плавающей запятой. Например,julia> a = -1; 2^a
выдаст такую ошибку, так как результат не целочисленный (подробные сведения см. в разделе FAQ об ошибках выхода за пределы области). -
Если в Julia нужно вернуть или присвоить несколько значений, используются кортежи, например
(a, b) = (1, 2)
илиa, b = 1, 2
. В Julia нет функцииnargout
, которая часто применяется в MATLAB для выполнения дополнительных действий в зависимости от количества возвращенных значений. В аналогичных целях можно использовать необязательные и именованные аргументы. -
В Julia есть истинные одномерные массивы. Векторы столбцов имеют размер
N
, а неNx1
. Например, выражениеrand(N)
создает одномерный массив. -
В 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]
). -
В Julia конструкции
a:b
иa:b:c
создают объектыAbstractRange
. Для создания полного вектора, как в MATLAB, используйте методcollect(a:b)
. Однако обычно вызыватьcollect
не нужно. ОбъектAbstractRange
в большинстве случаев ведет себя как обычный массив, однако он эффективнее благодаря «ленивому» вычислению значений. Этот шаблон создания специальных объектов вместо полных массивов применяется часто, в том числе с функциями, такими какrange
, и итераторами, такими какenumerate
иzip
. Специальные объекты, как правило, можно использовать как обычные массивы. -
Функции в Julia возвращают результат вычисления последнего выражения или выражения с ключевым словом
return
. Перечислять имена возвращаемых переменных в определении функции не нужно (подробные сведения см. в разделе Ключевое слово return). -
Скрипт в Julia может содержать сколько угодно функций, и все определения видны извне при загрузке файла. Определения функций можно загружать из файлов вне текущего рабочего каталога.
-
В Julia при вызове редукций, таких как
sum
,prod
иmax
, с одним аргументом, напримерsum(A)
, они выполняются применительно к каждому элементу массива, даже если уA
более одного измерения. -
В Julia при вызове функции без аргументов необходимо использовать круглые скобки, например
rand()
. -
В Julia не рекомендуется ставить точку с запятой в конце выражений. Результаты выражений не выводятся на экран автоматически (кроме как в интерактивной командной строке), поэтому строки кода нет необходимости заканчивать точкой с запятой. Для вывода на экран определенных данных можно использовать функцию
println
или макрос@printf
. -
В Julia, если
A
иB
— массивы, операции логического сравнения, такие какA == B
, не возвращают массив логических значений. Вместо этого используйтеA .== B
или аналогичные выражения для других логических операторов, таких как<
и>
. -
В Julia операторы
&
,|
и⊻
(xor
) выполняются побитово, так же, как, соответственно,and
,or
иxor
в MATLAB. Их приоритет такой же, как у побитовых операторов в Python (но не такой, как в C). Они могут применяться к скалярным значениям или поэлементно к массивам, а также использоваться для объединения логических массивов. Однако обратите внимание на различие в очередности операций: могут потребоваться круглые скобки (например, для выбора элементовA
, равных 1 или 2, используйте выражение(A .== 1) .| (A .== 2)
). -
В Julia элементы коллекции могут передаваться в функцию как аргументы с помощью оператора расширения (splat-оператора)
...
, напримерxs=[1,2]; f(xs...)
. -
Функция
svd
в Julia возвращает сингулярные значения в виде вектора, а не плотной диагональной матрицы. -
В Julia символ
...
не применяется для продолжения строк кода. Вместо этого неполное выражение автоматически продолжается в следующей строке. -
Как в Julia, так и в MATLAB переменной
ans
присваивается значение последнего выражения, введенного в рамках интерактивного сеанса. Однако в Julia, в отличие от MATLAB, переменнаяans
не задается, когда код на Julia выполняется в рамках неинтерактивного сеанса. -
Структуры (
struct
) в Julia не поддерживают динамическое добавление полей во время выполнения, в отличие от классов (class
) в MATLAB. Вместо этого используйтеDict
. Словари в Julia не упорядочиваются. -
В Julia каждый модуль имеет собственную глобальную область или пространство имен, в то время как в MATLAB глобальная область только одна.
-
В 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!
позволяет реже прибегать к временным массивам. -
Извлечение (или «разыменование») всех элементов массива ячеек, например
vertcat(A{:})
в MATLAB, записывается в Julia с помощью оператора расширения (splat-оператор), напримерvcat(A...)
. -
В Julia функция
adjoint
производит сопряженное транспонирование; в MATLABadjoint
предоставляет присоединенную матрицу (классическое сопряжение), то есть выполняет транспонирование матрицы алгебраических дополнений. -
В Julia выражение a^b^c вычисляется как a^(b^c), в то время как в MATLAB — как (a^b)^c.
Примечательные отличия от R
Одной из целей создания языка Julia было обеспечить эффективные возможности для анализа данных и статистического программирования. Тем, кто переходит с языка R на Julia, следует помнить о следующих важных отличиях.
-
В Julia в одинарные кавычки заключаются символы, а не строки.
-
В Julia подстроки можно создавать по индексам строки. В R перед созданием подстрок строки необходимо преобразовывать в векторы символов.
-
В Julia, так же как в Python, но не так, как в R, строки можно создавать с помощью тройных кавычек
""" ... """
. Этот синтаксис удобен для создания строк, содержащих разрывы. -
В Julia переменное количество аргументов указывается с помощью оператора расширения
...
, который обязательно должен стоять после имени определенной переменной. В R, напротив,...
можно использовать отдельно. -
В Julia остаток от целочисленного деления вычисляется с помощью
mod(a, b)
, а неa %% b
.%
в Julia — это оператор нахождения остатка. -
В Julia векторы создаются с помощью квадратных скобок.
[1, 2, 3]
в Julia — это то же самое, чтоc(1, 2, 3)
в R. -
В 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]
вызывает исключениеBoundsError
.
-
-
Как и во многих других языках, в Julia разрешены не все операции с векторами разной длины, в отличие от R, где достаточно, чтобы у векторов был общий диапазон индексов. Например, выражение
c(1, 2, 3, 4) + c(1, 2)
допустимо в R, но эквивалентное выражение[1, 2, 3, 4] + [1, 2]
в Julia вызовет ошибку. -
В Julia допускается необязательная запятая в конце, если она не меняет смысл кода. Пользователей R это может сбить с толку при обращении к элементам массивов по индексам. Например, выражение
x[1,]
в R возвращает первую строку матрицы, однако в Julia запятая игнорируется, поэтомуx[1,] == x[1]
и возвращает первый элемент. Чтобы получить строку, используйте:
, как в выраженииx[1,:]
. -
Функция
map
в Julia сначала принимает функцию, а затем ее аргументы, в отличие отlapply(<structure>, function, ...)
в R. Точно так же эквивалентом функции Rapply(X, MARGIN, FUN, ...)
в Julia будет функцияmapslices
, в которую другая функция передается первым аргументом. -
Многовариантное применение в R, например
mapply(choose, 11:13, 1:3)
, можно записать в Julia в видеbroadcast(binomial, 11:13, 1:3)
. Кроме того, для функций векторизацииbinomial.(11:13, 1:3)
в Julia предлагается синтаксис через точку. -
Для обозначения конца условных блоков, таких как
if
, блоков циклов, таких какwhile
иfor
, и функций в Julia используется ключевое словоend
. Вместо однострочной записиif ( cond ) statement
в Julia допустимы операторы видаif cond; statement; end
,cond && statement
и!cond || statement
. Операторы присваивания в двух последних синтаксических конструкциях должны явным образом заключаться в круглые скобки, напримерcond && (x = value)
. -
В Julia
<-
,<<-
и->
не являются операторами присваивания. -
Оператор
->
в Julia создает анонимную функцию. -
Оператор
*
в Julia позволяет перемножать матрицы, в отличие от R. ЕслиA
иB
— матрицы, тоA * B
в Julia означает умножение матриц. В R для этого требуется выражениеA %% B
. В R такая запись означает поэлементное (адамарово) произведение. В Julia для поэлементного умножения необходимо написатьA . B
. -
В Julia для транспонирования матриц служит функция
transpose
, а для сопряженного транспонирования — оператор'
или функцияadjoint
. Таким образом, выражениеtranspose(A)
в Julia равносильноt(A)
в R. Кроме того, для нерекурсивного транспонирования в 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
не интерпретируются как логические значения. Написатьif (1)
в Julia нельзя, так как операторif
принимает только логические значения. Вместо этого можно написатьif true
,if Bool(1)
илиif 1==1
. -
В Julia нет функций
nrow
иncol
. Вместоnrow(M)
используйтеsize(M, 1)
, а вместоncol(M)
—size(M, 2)
. -
В Julia скалярные значения, векторы и матрицы четко различаются. В R
1
иc(1)
— это одно и то же. В Julia же это не взаимозаменяемые выражения. -
В Julia нельзя присвоить результат вызову функции в левой части операции присваивания, то есть нельзя написать
diag(M) = fill(1, n)
. -
В Julia не рекомендуется вводить лишние функции в главное пространство имен. Большинство статистических функций Julia находятся в пакетах, предлагаемых группой JuliaStats. Например:
-
Функции, относящиеся к вероятностным распределениям, входят в пакет Distributions.
-
Пакет DataFrames предоставляет инструменты для работы с Data Frame.
-
Обобщенные линейные модели предоставляются пакетом GLM.
-
-
В Julia имеются кортежи и настоящие хэш-таблицы, но нет списков в стиле R. Для возврата нескольких элементов обычно следует использовать кортеж или именованный кортеж: вместо
list(a = 1, b = 2)
используйте(1, 2)
или(a=1, b=2)
. -
Julia поощряет пользователей к написанию собственных типов, которые проще в использовании, чем объекты S3 или S4 в R. Система множественной диспетчеризации в Julia означает, что выражения
table(x::TypeA)
иtable(x::TypeB)
работают так же, какtable.TypeA(x)
иtable.TypeB(x)
в R. -
В Julia присваиваемые или передаваемые в функцию значения не копируются. Если функция изменяет массив, эти изменения будут видны вызывающей стороне. Это серьезное отличие от R, которое позволяет новым функциям оперировать с большими структурами данных гораздо эффективнее.
-
В Julia конкатенация векторов и матриц производится с помощью функций
hcat
,vcat
иhvcat
, а неc
,rbind
иcbind
, как в R. -
В Julia диапазон наподобие
a:b
является не краткой формой записи вектора, как в R, а специальным объектомAbstractRange
, применяемым для итерации. Преобразовать диапазон в вектор можно так:collect(a:b)
. -
Оператор
:
имеет разный приоритет в R и Julia. В частности, в Julia арифметические операторы имеют приоритет над оператором:
, в то время как в R верно обратное. Например, выражение1:n-1
в Julia эквивалентно1:(n-1)
в R. -
Функции
max
иmin
в Julia эквивалентны соответственноpmax
иpmin
в R, но аргументы должны иметь одинаковую размерность. Хотя функцииmaximum
иminimum
являются аналогамиmax
иmin
в R, есть важные различия. -
Функции
sum
,prod
,maximum
иminimum
в Julia отличаются от своих аналогов в R. Все они принимают необязательный именованный аргументdims
, означающий измерения, с которыми производится операция. Например, возьмем одинаковые матрицыA = [1 2; 3 4]
в Julia иB <- rbind(c(1,2),c(3,4))
в R. Тогда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 есть ряд функций, которые могут изменять свои аргументы. Например, есть пара функций
sort
иsort!
. -
В R для обеспечения производительности требуется векторизация. В Julia ситуация почти обратная: зачастую быстрее выполняется код, в котором применяются невекторизированные циклы.
-
В Julia вычисления производятся немедленно; «ленивые» вычисления в стиле R не поддерживаются. Для большинства пользователей это означает редкое применение выражений без кавычек или имен столбцов.
-
В Julia не поддерживается тип
NULL
. Ближайший аналог — константаnothing
, но она действует как скалярное значение, а не список. Вместоis.null(x)
используйтеx === nothing
. -
В Julia отсутствующие значения представлены объектом
missing
, а неNA
. Вместо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>3]
для измененияx
на месте. В Julia с этой целью предоставляются функции более высокого порядкаfilter
иfilter!
, которые позволяют использовать выраженияfilter(z->z>3, x)
иfilter!(z->z>3, x)
в качестве альтернативы дляx[x.>3]
иx = x[x.>3]
. Функцияfilter!
позволяет реже прибегать к временным массивам.
Примечательные отличия от Python
-
Блоки
for
,if
,while
и т. д. в Julia заканчиваются ключевым словомend
. Уровень отступа не имеет значения, как в Python. В отличие от Python, в Julia нет ключевого словаpass
. -
В Julia строки обозначаются двойными кавычками, например
"text"
(многострочный текст заключается в три пары двойных кавычек), в то время как в Python они могут обозначаться либо одинарными ('text'
), либо двойными кавычками ("text"
). В Julia одинарные кавычки используются для символов ('c'
). -
Для конкатенации строк в Julia служит оператор
*
, а не+
, как в Python. Аналогичным образом, для повтора строк применяется^
, а не*
. Неявная конкатенация строковых литералов, возможная в Python (например,'ab' 'cd' == 'abcd'
), в Julia не поддерживается. -
Списки Python — гибкие, но медленные — соответствуют в Julia типу
Vector{Any}
или в более общем случае типуVector{T}
, гдеT
— некоторый неконкретный тип элементов. «Быстрые» массивы, такие как массивы NumPy, элементы которых хранятся на месте (то есть типdtype
—np.float64
,[('f1', np.uint64), ('f2', np.int32)]
и т. д.), могут быть представлены массивамиArray{T}
, гдеT
— конкретный, неизменяемый тип элементов. Сюда входят как встроенные типы, напримерFloat64
,Int32
иInt64
, так и более сложные типы, напримерTuple{UInt64,Float64}
, а также многие пользовательские типы. -
В Julia индексирование массивов, строк и т. д. начинается с единицы, а не с нуля.
-
При индексировании срезов в Julia последний элемент включается, в отличие от Python.
a[2:3]
в Julia равносильноa[1:3]
в Python. -
В отличие от Python, в Julia допустимы массивы AbstractArray с произвольными индексами. Отрицательные индексы, имеющие особый смысл в Python, например
a[-1]
иa[-2]
, должны записываться в Julia в видеa[end]
иa[end-1]
. -
Для индексирования до последнего элемента в Julia требуется ключевое слово
end
.x[1:]
в Python эквивалентноx[2:end]
в Julia. -
В Julia символ
:
перед объектом создаетSymbol
или заключает выражение в кавычки, поэтомуx[:5]
равносильноx[5]
. Чтобы получить первыеn
элементов массива, используйте индексирование диапазонов. -
В Julia индексирование диапазона имеет формат
x[start:step:stop]
, а в Python — форматx[start:(stop+1):step]
. Поэтомуx[0:10:2]
в Python эквивалентноx[1:2:10]
в Julia. Аналогичным образом, выражениеx[::-1]
, означающее в Python перевернутый массив, эквивалентноx[end:-1:1]
в Julia. -
В Julia диапазоны можно создавать отдельно в виде
start:step:stop
, то есть с помощью того же синтаксиса, что и при индексировании массивов. Кроме того, поддерживается функцияrange
. -
В Julia при индексировании матрицы с помощью массивов, например
X[[1,2], [1,3]]
, происходит обращение к подматрице, являющейся пересечением первой и второй строк с первым и третьим столбцами. В Python выражениеX[[1,2], [1,3]]
означает обращение к вектору, содержащему значения ячеек[1,1]
и[2,3]
матрицы. ВыражениеX[[1,2], [1,3]]
в Julia эквивалентноX[np.ix_([0,1],[0,2])]
в Python. ВыражениеX[[0,1], [0,2]]
в Python эквивалентноX[[CartesianIndex(1,1), CartesianIndex(2,3)]]
в Julia. -
В Julia нет синтаксиса продолжения строк: если содержимое строки представляет собой полноценное выражение, то строка считается завершенной; в противном случае она продолжается. Один из способов продолжить выражение — заключить его в круглые скобки.
-
В Julia массивы развертываются по столбцам (как в Фортране), в то время как массивы NumPy по умолчанию развертываются по строкам (как в C). Для оптимальной производительности при переборе массивов порядок циклов в Julia должен быть обратным по сравнению с NumPy (см. соответствующий раздел главы «Советы по производительности»).
-
Операторы с присваиванием в 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)
возвращает[1,2,3]
каждый раз, когда она вызывается какg()
. -
В Julia именованные аргументы должны передаваться с указанием имен, в отличие от Python, где их обычно можно передавать как позиционные. Передача именованного аргумента как позиционного изменяет сигнатуру метода, что приводит к созданию исключения
MethodError
или вызову неверного метода. -
В Julia
%
— это оператор нахождения остатка, в то время как в Python — оператор нахождения остатка от целочисленного деления. -
В Julia часто используемый тип
Int
соответствует машинному целочисленному типу (Int32
илиInt64
), в то время как в Pythonint
— это целое число произвольной длины. Это означает, что в Julia типInt
может переполняться, так что2^64 == 0
. Если вам нужно большее значение, выберите другой подходящий тип, напримерInt128
,BigInt
или тип с плавающей запятой, такой какFloat64
. -
Мнимая единица
sqrt(-1)
представлена в Julia константойim
, а неj
, как в Python. -
В Julia оператором возведения в степень является
^
, а не**
, как в Python. -
Для представления пустого значения в Julia служит константа
nothing
типаNothing
, а в Python — объектNone
типаNoneType
. -
В Julia при применении стандартного оператора к матричному типу выполняется матричная операция, в то время как в Python — поэлементная операция. Если и
A
, иB
— матрицы,A * B
в Julia производит умножение матриц, а не поэлементное умножение, как в Python. ВыражениеA * B
в Julia равносильноA @ B
в Python, аA * B
в Python равносильноA .* B
в Julia. -
Оператор сопряжения
'
в Julia возвращает сопряжение вектора («ленивое» представление строчного вектора), в то время как оператор транспонирования.T
применительно к вектору в Python возвращает исходный вектор (холостая операция). -
В Julia функция может иметь несколько конкретных реализаций (называемых методами), выбор из которых производится посредством множественной диспетчеризации на основе типов всех аргументов в вызове. В отличие от этого, функция в Python имеет единственную реализацию, и полиморфизм невозможен (для вызовов методов в Python применяется другой синтаксис, допускающий диспетчеризацию на стороне получателя метода).
-
В Julia нет классов. Вместо этого есть структуры (изменяемые или неизменяемые), содержащие данные, но не методы.
-
Вызов метода экземпляра класса в Python (
x = MyClass(*args); x.f(y)
) соответствует вызову функции в Julia, напримерx = MyType(args...); f(x, y)
. В целом множественная диспетчеризация является более гибкой и эффективной, чем система классов в Python. -
У структуры Julia может быть только один абстрактный супертип, в то время как классы Python могут наследоваться от одного или нескольких суперклассов (абстрактных или конкретных).
-
В Julia логическая структура программы (пакеты и модули) не зависит от структуры файлов (для включения дополнительных файлов используется ключевое слово
include
), в то время как в Python структура кода определяется каталогами (пакетами) и файлами (модулями). -
Тернарный оператор
x > 0 ? 1 : -1
в Julia соответствует в Python условному выражению1 if x > 0 else -1
. -
В Julia символ
@
означает макрос, а в Python — декоратор. -
Обработка исключений в Julia осуществляется с помощью конструкции
try
—catch
—finally
, а неtry
—except
—finally
. В отличие от Python, в Julia не рекомендуется использовать обработку исключений в рамках обычного порядка выполнения (по сравнению с Python в Julia обычный порядок выполнения быстрее, но исключения перехватываются медленнее). -
В Julia циклы выполняются быстро, поэтому писать векторизированный код для повышения производительности не нужно.
-
Будьте осторожны с неконстантными глобальными переменными в Julia, особенно в сплошных циклах. Так как в Julia (в отличие от Python) можно писать код, близкий к машинному, использование глобальных переменных может иметь серьезные последствия (см. главу Советы по производительности).
-
В Julia округление и усечение производятся явным образом. Вместо выражения Python
int(3.7)
следует использоватьfloor(Int, 3.7)
илиInt(floor(3.7))
, причем они отличаются отround(Int, 3.7)
.floor(x)
иround(x)
сами по себе возвращают целочисленное значение того же типа, что иx
, вместоInt
. -
В Julia анализ текста выполняется явным образом. Выражение Python
float("3.7")
должно записываться в Julia в видеparse(Float64, "3.7")
. -
В Python большинство значений можно использовать как логические (например,
if "a":
означает, что последующий блок выполняется, аif "":
— что он не выполняется). В Julia требуется явное преобразование в типBool
(например,if "a"
вызовет исключение). Для проверки непустой строки в Julia нужно написать явным образомif !isempty("")
. Возможно, это покажется странным, но в Pythonif "False"
иbool("False")
дают результатTrue
(потому что"False"
— это непустая строка); в Juliaparse(Bool, "false")
возвращаетfalse
. -
В Julia большинство блоков кода, включая циклы и конструкции
try
—catch
—finally
, образуют новую локальную область. Обратите внимание, что включения (списковые, генераторы и т. д.) образуют новую локальную область как в Python, так и в Julia, в то время как блокиif
не образуют ее в обоих языках.
Примечательные отличия от C/C++
-
Индексы массивов в Julia указываются в квадратных скобках и могут иметь несколько измерений:
A[i,j]
. Это не просто синтаксический сахар для ссылки на указатель или адрес, как в C/C++. См. раздел руководства, посвященный созданию массивов. -
В Julia индексирование массивов, строк и т. д. начинается с единицы, а не с нуля.
-
Когда массив в Julia присваивается другой переменной, он не копируется. После присваивания
A = B
изменение элементовB
приведет к их изменению вA
. Операторы с присваиванием, например+=
, выполняются не на месте. Они эквивалентныA = A + B
, то есть левая часть привязывается к результату выражения в правой части. -
В Julia массивы развертываются по столбцам (как в Фортране), в то время как в C/C++ они по умолчанию развертываются по строкам. Для оптимальной производительности при переборе массивов порядок циклов в Julia должен быть обратным по сравнению с C/C++ (см. соответствующий раздел главы «Советы по производительности»).
-
В Julia присваиваемые или передаваемые в функцию значения не копируются. Если функция изменяет массив, эти изменения будут видны вызывающей стороне.
-
В Julia, в отличие от C/C++, пробелы имеют значение, поэтому их следует добавлять в программу Julia или удалять из нее с осторожностью.
-
В Julia числовые литералы без десятичного разделителя (например,
42
) создают целые числа со знаком типаInt
, но если литерал не помещается в машинное слово, он автоматически продвигается до типа большего размера, напримерInt64
(еслиInt
— это типInt32
),Int128
или типаBigInt
произвольного размера. Суффиксов числовых литералов, означающих наличие знака, таких какL
,LL
,U
,UL
илиULL
, нет. Десятичные литералы всегда имеют знак, а шестнадцатеричные (начинающиеся с0x
, как в C/C++) не имеют, если только они не кодируют более 128 битов — в этом случае они имеют типBigInt
. Кроме того, в отличие от C/C++ и Java, а также десятичных литералов в Julia, тип шестнадцатеричного литерала зависит от его длины, включая начальные символы «0s». Например,0x0
и0x00
имеют типUInt8
,0x000
и0x0000
— типUInt16
, литералы с 5—8 шестнадцатеричными цифрами — типUInt32
, с 9—16 шестнадцатеричными цифрами — типUInt64
, с 17—32 шестнадцатеричными цифрами — типUInt128
, а с более чем 32 шестнадцатеричными цифрами — типBigInt
. Это следует учитывать при определении шестнадцатеричных масок. Например,~0xf == 0xf0
сильно отличается от~0x000f == 0xfff0
. Битовые литералы длиной 64 бита (Float64
) и 32 бита (Float32
) выражаются как1.0
и1.0f0
соответственно. Литералы с плавающей запятой округляются (и не продвигаются до типаBigFloat
), если их нельзя представить в точности. Литералы с плавающей запятой близки по своему поведению к C/C++. Восьмеричные литералы (с префиксом0o
) и двоичные литералы (с префиксом0b
) также относятся к типам без знака (или к типуBigInt
, если длина более 128 бит). -
В Julia оператор деления
/
возвращает число с плавающей запятой, если оба операнда целочисленного типа. Для целочисленного деления используйте функциюdiv
или оператор÷
. -
Индексирование массива (
Array
) с использованием типов с плавающей запятой обычно вызывает ошибку в Julia. Эквивалентом выражения Ca[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++ (для этой цели в 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
не интерпретируются как логические значения. Написатьif (1)
в Julia нельзя, так как операторif
принимает только логические значения. Вместо этого можно написатьif true
,if Bool(1)
илиif 1==1
. -
Для обозначения конца условных блоков, таких как
if
, блоков циклов, таких какwhile
иfor
, и функций в Julia используется ключевое словоend
. Вместо однострочной записиif ( cond ) statement
в Julia допустимы операторы видаif cond; statement; end
,cond && statement
и!cond || statement
. Операторы присваивания в двух последних синтаксических конструкциях должны явным образом заключаться в круглые скобки, напримерcond && (x = value)
, из-за приоритета операторов. -
В Julia нет синтаксиса продолжения строк: если содержимое строки представляет собой полноценное выражение, то строка считается завершенной; в противном случае она продолжается. Один из способов продолжить выражение — заключить его в круглые скобки.
-
Макросы Julia работают с проанализированными выражениями, а не с текстом программы, что позволяет им выполнять сложные преобразования кода Julia. Имена макросов начинаются с символа
@
. Возможен синтаксис наподобие функций (@mymacro(arg1, arg2, arg3)
) и наподобие операторов (@mymacro arg1 arg2 arg3
). Эти формы взаимозаменяемые: синтаксис наподобие функций особенно полезен, если макрос включен в другое выражение, и обычно понятнее. Синтаксис наподобие операторов часто применяется для аннотирования блоков, как в распределенной конструкцииfor
:@distributed for i in 1:n; #= body =#; end
. Если может быть непонятно, где заканчивается конструкция макроса, используйте синтаксис наподобие функций. -
В Julia есть тип перечисления, который выражается с помощью макроса
@enum(name, value1, value2, ...)
. Пример:@enum(Fruit, banana=1, apple, pear)
-
По соглашению имена функций, которые изменяют свои аргументы, заканчиваются символом
!
, напримерpush!
. -
В C++ по умолчанию применяется статическая диспетчеризация. Чтобы использовать динамическую диспетчеризацию, необходимо аннотировать функцию как виртуальную. Напротив, в Julia все методы являются «виртуальными» (это следует понимать в более широком смысле, так как методы диспетчеризуются на основе всех типов аргументов, а не только
this
, по правилу наиболее специфичного объявления).
Julia ⇔ C/C++: Пространства имен
-
Пространства имен (
namespace
) в C/C++ примерно соответствуют модулям (module
) в Julia. -
Частных глобальных переменных или полей в Julia нет. Все объекты являются общедоступными через полные пути (или при желании относительные).
-
using MyNamespace::myfun
(C++) примерно соответствуетimport MyModule: myfun
(Julia). -
using namespace MyNamespace
(C++) примерно соответствуетusing MyModule
(Julia)-
В Julia только экспортированные (с помощью оператора
export
) символы доступны вызывающему модулю. -
В C++ доступны только элементы, указанные во включенных (общедоступных) файлах заголовков.
-
-
Обратите внимание: ключевые слова
import
иusing
в Julia также служат для загрузки модулей (см. ниже). -
Обратите внимание: ключевые слова
import
иusing
в Julia действуют только на уровне глобальной области (module
)-
В C++
using namespace X
действует в произвольных областях (например, в области функции).
-
Julia ⇔ C/C++: Загрузка модулей
-
Если в Julia вам нужно то, что в C/C++ называется библиотекой, то, скорее всего, это будет пакет.
-
Обратите внимание: библиотеки в C/C++ часто вмещают в себя несколько «программных модулей», в то время как в Julia пакет обычно содержит только один такой модуль.
-
Напоминание: модули (
module
) в Julia — это глобальные области (необязательно «программные модули»).
-
-
Вместо скриптов сборки или создания (
make
) в Julia используются «среды проектов» (иногда называемые просто «проектом» или «средой»).-
Скрипты сборки нужны только для более сложных приложений (например, требующих компиляции или скачивания исполняемых файлов C/C++).
-
Для разработки приложения или проекта в Julia можно инициализировать его корневой каталог как «среду проекта» и поместить в него относящиеся к приложению код и пакеты. Это обеспечивает хороший контроль над зависимостями проекта и воспроизводимость в будущем.
-
Доступные пакеты добавляются в «среду проекта» с помощью функции
Pkg.add()
или режима Pkg в REPL. (Однако пакет при этом не загружается.) -
Список доступных пакетов (прямых зависимостей) для «среды проекта» сохраняется в ее файле
Project.toml
. -
Полная информация о зависимостях для «среды проекта» генерируется автоматически и сохраняется в файле
Manifest.toml
посредствомPkg.resolve()
.
-
-
Пакеты («программные модули»), доступные «среде проекта», загружаются с помощью оператора
import
илиusing
.-
В C/C++ для получения объявлений объектов и функций и их компоновки в библиотеках при сборке исполняемого файла используется оператор
#include <moduleheader>
. -
В Julia при повторном вызове using или import существующий модуль просто вводится в область, но не загружается снова (так же, как при добавлении нестандартных директив
#pragma once
в C/C++).
-
-
Доступ к репозиториям пакетов на основе каталогов в Julia можно обеспечить путем добавления путей к репозиториям в массив
Base.LOAD_PATH
.-
Перед загрузкой пакетов из репозиториев на основе каталогов с помощью оператора
import
илиusing
не требуется использоватьPkg.add()
. Они изначально доступны в рамках проекта. -
Репозитории пакетов на основе каталогов — это самое быстрое решение для разработки локальных библиотек «программных модулей».
-
Julia ⇔ C/C++: Сборка модулей
-
В C/C++ файлы
.c
и.cpp
компилируются и добавляются в библиотеку с помощью скриптов сборки или создания (make
).-
В Julia операторы
import [PkgName]
иusing [PkgName]
загружают файл[PkgName].jl
из подкаталога[PkgName]/src/
пакета. -
В свою очередь,
[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 («программном модуле»). Благодаря этому достаточно легко обеспечить однократное включение (include
) файлов (нет путаницы с#ifdef
).
-
Julia ⇔ C/C++: Интерфейс модуля
-
В C++ интерфейсы предоставляются с помощью «общедоступных» файлов
.h
и.hpp
, в то время как в Julia модули (module
) экспортируют (export
) символы, предназначенные для пользователей.-
В Julia модули (
module
) зачастую просто добавляют функциональность путем создания новых «методов» существующих функций (например,Base.push!
). -
Поэтому разработчики на Julia не могут полагаться на файлы заголовков в качестве документации по интерфейсу.
-
Интерфейсы для пакетов Julia обычно описываются в строках docstring, файлах README.md, на статических веб-страницах и т. д.
-
-
Некоторые разработчики предпочитают не экспортировать (
export
) все символы, необходимые для использования пакета или модуля.-
Для доступа к этим компонентам пользователю может потребоваться указать функцию, структуру и т. д. с именем пакета или модуля (например,
MyModule.run_this_task(...)
).
-
Julia ⇔ C/C++: Краткая справка
Программная концепция | Julia | C/C++ |
---|---|---|
неименованная область |
|
|
область функции |
|
|
глобальная область |
|
|
программный модуль |
«пакет» в Julia |
файлы |
сборка |
|
|
импорт |
|
|
библиотека модулей |
|
дополнительные файлы |
-
Диспетчер пакетов в Julia поддерживает регистрацию нескольких пакетов из одного репозитория Git.
-
Это позволяет пользователям размещать библиотеку связанных пакетов в одном репозитории.
-
Реестры Julia в первую очередь предназначены для управления версиями пакетов и их распространения.
-
С помощью пользовательских реестров пакетов можно создать тип библиотеки модулей.
-
Примечательные отличия от Common Lisp
-
В Julia массивы по умолчанию индексируются от единицы и поддерживают произвольные смещения индексов.
-
Функции и переменные находятся в одном пространстве имен (Lisp-1).
-
Есть тип
Pair
, но его назначение отличается отCOMMON-LISP:CONS
. В большинстве случаев (при разделении, построении кортежей и т. д.) различные итерируемые коллекции могут использоваться как взаимозаменяемые. В случае с небольшими коллекциями разнородных элементов типTuple
ближе всего к спискам Common Lisp. Вместо ассоциативных списков используйтеNamedTuple
. Для коллекций, имеющих больший размер или содержащих разнородные элементы, следует использовать типыArray
иDict
. -
Типичный рабочий процесс прототипирования в Julia также предполагает непрерывное манипулирование образом, реализуемое пакетом Revise.jl.
-
Из соображений производительности операции в Julia обычно предполагают устойчивость типов. Тогда как для Common Lisp характерно абстрагирование от базовых машинных операций, операции в Julia более тесно согласуются с ними. Пример: — Целочисленное деление с помощью
/
всегда возвращает результат с плавающей запятой, даже если он точный. — Оператор//
всегда возвращает рациональный результат. — Оператор÷
всегда возвращает (усеченный) целочисленный результат. — Сверхбольшие числа поддерживаются, но преобразование не выполняется автоматически; по умолчанию происходит переполнение целочисленных значений. — Комплексные числа поддерживаются, но для получения комплексного результата требуются комплексные входные значения. — Имеется несколько комплексных и рациональных типов с разными типами компонентов. -
Модули (пространства имен) могут быть иерархическими. Ключевые слова
import
иusing
имеют двойное назначение: они загружают код и делают его доступным в пространстве имен. Импорт с помощьюimport
возможен только по имени модуля (что примерно соответствуетASDF:LOAD-OP
). Имена слотов не нужно экспортировать отдельно. Глобальным переменным нельзя присваивать значения извне модуля (обойти это ограничение можно с помощью вызоваeval(mod, :(var = val))
). -
Имеются макросы (начинаются с символа
@
), но они не так тесно интегрированы в язык, как в Common Lisp, поэтому и применяются не так широко. Язык поддерживает гигиену макросов. Из-за различий в поверхностном синтаксисе эквивалентаCOMMON-LISP:&BODY
нет. -
Все функции являются универсальными и используют множественную диспетчеризацию. Списки аргументов не обязательно должны следовать одному и тому же шаблону, что делает возможной эффективную идиоматику (см. описание ключевого слова
do
). Необязательные и именованные аргументы обрабатываются по-разному. Неоднозначность методов не разрешается, как в Common Lisp Object System, из-за чего для пересечения требуется определение более конкретного метода. -
Символы не относятся в какому-либо пакету и сами по себе не содержат значений.
M.var
вычисляет символvar
в модулеM
. -
Функциональный стиль программирования полностью поддерживается, включая замыкания, но для Julia он не характерен. При изменении захваченных переменных могут потребоваться некоторые обходные приемы для обеспечения производительности.