Noteworthy Differences from other Languages
Noteworthy differences from 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]
, соответствующее выражение Juliaa[5] = 7
вызывает ошибку, если длинаa
меньше 5 или если это выражение является первым использованием идентификатораa
. Julia имеетpush!
иappend!
, которые увеличиваютVector
s гораздо эффективнее, чем MATLAB'sa(end+1) = val
.Воображаемая единица
sqrt(-1)
в Julia представлена какim
, а неi
илиj
, как в MATLAB.В Julia литеральные числа без десятичной точки (такие как
42
) создают целые числа вместо чисел с плавающей запятой. В результате некоторые операции могут вызывать ошибку области, если они ожидают число с плавающей запятой; например,julia> a = -1; 2^a
вызывает ошибку области, так как результатом не является целое число (см. the FAQ entry on domain errors для получения подробной информации).В Julia несколько значений возвращаются и присваиваются в виде кортежей, например,
(a, b) = (1, 2)
илиa, b = 1, 2
.nargout
в MATLAB, который часто используется в MATLAB для выполнения необязательной работы в зависимости от количества возвращаемых значений, не существует в Julia. Вместо этого пользователи могут использовать необязательные и именованные аргументы для достижения аналогичных возможностей.Джулия имеет истинные одномерные массивы. Столбцовые векторы имеют размер
N
, а неNx1
. Например,rand(N)
создает одномерный массив.В Julia,
[x,y,z]
всегда создаст массив из 3 элементов, содержащий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
, вместо того чтобы перечислять имена переменных для возврата в определении функции (см. The return Keyword для подробностей).Скрипт на Julia может содержать любое количество функций, и все определения будут видны извне, когда файл загружен. Определения функций могут быть загружены из файлов вне текущего рабочего каталога.
В Julia редукции, такие как
sum
,prod
иmaximum
, выполняются для каждого элемента массива, когда вызываются с одним аргументом, как вsum(A)
, даже еслиA
имеет более одной размерности.В Julia круглые скобки должны использоваться для вызова функции без аргументов, как в
rand()
.Джулия не рекомендует использовать точки с запятой для завершения операторов. Результаты операторов не выводятся автоматически (за исключением интерактивного режима), и строки кода не обязательно должны заканчиваться точками с запятой.
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 элементы коллекции могут быть переданы в качестве аргументов функции с использованием оператора распаковки
...
, как вxs=[1,2]; f(xs...)
.Julia's
svd
возвращает сингулярные значения в виде вектора, а не в виде плотной диагональной матрицы.В Julia
...
не используется для продолжения строк кода. Вместо этого незавершенные выражения автоматически продолжаются на следующей строке.В Julia и MATLAB переменная
ans
устанавливается в значение последнего выражения, выданного в интерактивной сессии. В Julia, в отличие от MATLAB,ans
не устанавливается, когда код Julia выполняется в неинтерактивном режиме.Структуры
struct
в Julia не поддерживают динамическое добавление полей во время выполнения, в отличие от классовclass
в MATLAB. Вместо этого используйтеDict
. Словарь (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]
. Использование4d61726b646f776e2e436f64652822222c202266696c746572212229_40726566
уменьшает использование временных массивов.Аналог извлечения (или "разыменования") всех элементов массива ячеек, например, в
vertcat(A{:})
в MATLAB, записывается с использованием оператора распаковки в Julia, например, какvcat(A...)
.В Julia функция
adjoint
выполняет сопряженное транспонирование; в MATLABadjoint
предоставляет "адъюгат" или классический адъюгат, который является транспонированной матрицей кофакторов.В Julia выражение a^b^c вычисляется как a^(b^c), в то время как в MATLAB это (a^b)^c.
Noteworthy differences from R
Одной из целей Джули является предоставление эффективного языка для анализа данных и статистического программирования. Для пользователей, переходящих на Джули с R, вот некоторые примечательные отличия:
Одинарные кавычки в Julia заключают символы, а не строки.
Джулия может создавать подстроки, индексируя строки. В R строки должны быть преобразованы в векторы символов перед созданием подстрок.
В Julia, как и в Python, но в отличие от R, строки можно создавать с помощью тройных кавычек
""" ... """
. Этот синтаксис удобен для создания строк, содержащих переносы строк.В Julia аргументы переменной длины указываются с помощью оператора распаковки
...
, который всегда следует за именем конкретной переменной, в отличие от R, где...
может встречаться в изоляции.В Julia модуль является
mod(a, b)
, а неa %% b
.%
в Julia — это оператор остатка.Юлия создает векторы с помощью скобок. Юлиевский
[1, 2, 3]
эквивалентен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][[true, false]]
вызываетBoundsError
. - В Julia,
[1, 2, 3, 4][[true, false, true, false]]
возвращает[1, 3]
.
- В R,
Как и многие языки, 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,:]
.Julia's
map
принимает функцию первой, а затем её аргументы, в отличие отlapply(<structure>, function, ...)
в R. Аналогично, эквивалент Julia дляapply(X, MARGIN, FUN, ...)
в R - этоmapslices
, где функция является первым аргументом.Мультивариантное применение в R, например,
mapply(choose, 11:13, 1:3)
, можно записать какbroadcast(binomial, 11:13, 1:3)
в Julia. Эквивалентно, Julia предлагает более короткий синтаксис с точкой для векторизации функцийbinomial.(11:13, 1:3)
.Джулия использует
end
для обозначения конца условных блоков, таких какif
, блоков циклов, таких какwhile
/for
, и функций. Вместо однострочногоif ( cond ) statement
, Джулия позволяет использовать конструкции видаif cond; statement; end
,cond && statement
и!cond || statement
. Операции присваивания в последних двух синтаксисах должны быть явно обернуты в скобки, например,cond && (x = value)
.В Julia
<-
,<<-
и->
не являются операторами присваивания.Анонимная функция создается с помощью
->
в Julia.Оператор Julia
*
может выполнять матричное умножение, в отличие от R. ЕслиA
иB
- это матрицы, тоA * B
обозначает матричное умножение в Julia, эквивалентноеA %*% B
в R. В R эта же нотация выполняла бы поэлементное (хадамардово) произведение. Чтобы получить операцию поэлементного умножения, вам нужно написатьA .* B
в Julia.Джулия выполняет транспонирование матриц с помощью функции
transpose
и сопряженное транспонирование с помощью оператора'
или функцииadjoint
. Таким образом,transpose(A)
в Джулии эквивалентноt(A)
в R. Кроме того, некорректное транспонирование в Джулии обеспечивается функциейpermutedims
.Julia не требует скобок при написании операторов
if
или цикловfor
/while
: используйтеfor i in [1, 2, 3]
вместоfor (i in c(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
. Вместо этого используйтеsize(M, 1)
дляnrow(M)
иsize(M, 2)
дляncol(M)
.Джулия осторожно различает скаляры, векторы и матрицы. В R
1
иc(1)
одинаковы. В Джулии их нельзя использовать взаимозаменяемо.Юлия не может присваивать значения результатам вызовов функций с левой стороны операции присваивания: вы не можете написать
diag(M) = fill(1, n)
.Джулия не рекомендует заполнять главное пространство имен функциями. Большинство статистических функций для Джулии можно найти в packages под JuliaStats organization. Например:
- Функции, относящиеся к распределениям вероятностей, предоставляются Distributions package.
- DataFrames package предоставляет данные фреймы.
- Обобщенные линейные модели предоставляются GLM package.
Джулия предоставляет кортежи и реальные хэш-таблицы, но не списки в стиле R. При возврате нескольких элементов вы обычно должны использовать кортеж или именованный кортеж: вместо
list(a = 1, b = 2)
используйте(1, 2)
или(a=1, b=2)
.Джулия поощряет пользователей создавать свои собственные типы, которые проще использовать, чем объекты S3 или S4 в R. Множественная диспетчеризация в Джулии означает, что
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
являются эквивалентамиpmax
иpmin
соответственно в R, но оба аргумента должны иметь одинаковые размеры. В то время какmaximum
иminimum
заменяютmax
иmin
в R, существуют важные различия.sum
,prod
,maximum
иminimum
отличаются от своих аналогов в 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)
. Следует отметить, что нет проверки ошибок относительно второго аргумента.Джулия имеет несколько функций, которые могут изменять свои аргументы. Например, у нее есть как
sort
, так иsort!
.В R производительность требует векторизации. В Julia почти наоборот: наилучшие результаты достигаются часто с помощью девекторизованных циклов.
Julia оценивается eagerly и не поддерживает ленивую оценку в стиле R. Для большинства пользователей это означает, что существует очень мало нецитируемых выражений или имен столбцов.
Julia не поддерживает тип
NULL
. Ближайший эквивалент — этоnothing
, но он ведет себя как скалярное значение, а не как список. Используйтеx === nothing
вместоis.null(x)
.В Julia отсутствующие значения представлены объектом
missing
, а неNA
. Используйтеismissing(x)
(илиismissing.(x)
для поэлементной операции над векторами) вместоis.na(x)
. Функцияskipmissing
обычно используется вместоna.rm=TRUE
(хотя в некоторых конкретных случаях функции принимают аргументskipmissing
).Julia не имеет эквивалента функции
assign
илиget
из R.В 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]
. Использование4d61726b646f776e2e436f64652822222c202266696c746572212229_40726566
уменьшает использование временных массивов.
Noteworthy differences from Python
- Блоки
for
,if
,while
и т.д. в Julia завершаются ключевым словомend
. Уровень отступа не имеет значения, как это происходит в Python. В отличие от Python, в Julia нет ключевого словаpass
. - Строки обозначаются двойными кавычками (
"text"
) в Julia (с тремя двойными кавычками для многострочных строк), в то время как в Python они могут обозначаться либо одинарными ('text'
), либо двойными кавычками ("text"
). Одинарные кавычки используются для символов в Julia ('c'
). - Конкатенация строк выполняется с помощью
*
в Julia, а не+
, как в Python. Аналогично, повторение строк выполняется с помощью^
, а не*
. Неявная конкатенация строковых литералов, как в Python (например,'ab' 'cd' == 'abcd'
), не выполняется в Julia. - Python Lists—гибкие, но медленные—соответствуют типу 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 индексация массивов, строк и т.д. начинается с 1, а не с 0.
- Индексация срезов в Julia включает последний элемент, в отличие от Python.
a[2:3]
в Julia соответствуетa[1:3]
в Python. - В отличие от Python, Julia позволяет AbstractArrays with arbitrary indexes. Специальная интерпретация отрицательной индексации в Python,
a[-1]
иa[-2]
, должна быть записана какa[end]
иa[end-1]
в Julia. - Джулия требует
end
для индексации до последнего элемента.x[1:]
в Python эквивалентноx[2:end]
в Джулии. - В 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]]
, относится к подматрице, которая содержит пересечения первых и вторых строк с первыми и третьими столбцами. В PythonX[[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 имеют столбцовую порядок (упорядочены по Fortran), в то время как массивы NumPy имеют построчный порядок (упорядочены по C) по умолчанию. Для достижения оптимальной производительности при переборе массивов порядок циклов должен быть изменен в Julia по сравнению с NumPy (см. relevant section of Performance Tips).
- Операторы обновления в Julia (например,
+=
,-=
, ...) не являются ин-местными, в то время как в NumPy они таковыми являются. Это означает, чтоA = [1, 1]; B = A; B += [3, 3]
не изменяет значения вA
, а скорее переназначает имяB
на результат правой частиB = B + 3
, что является новым массивом. Для ин-местной операции используйтеB .+= 3
(см. также dot operators), явные циклы илиInplaceOps.jl
. - Джулия оценивает значения по умолчанию аргументов функции каждый раз, когда метод вызывается, в отличие от 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
), в отличие от Python, гдеint
является целым числом произвольной длины. Это означает, что в Julia типInt
будет переполняться, так что2^64 == 0
. Если вам нужны большие значения, используйте другой подходящий тип, такой какInt128
,BigInt
или тип с плавающей запятой, такой какFloat64
. - Мнимую единицу
sqrt(-1)
в Julia представляют какim
, а неj
, как в Python. - В Julia оператор возведения в степень — это
^
, а не**
, как в Python. - Джулия использует
nothing
типаNothing
для представления нулевого значения, в то время как Питон использует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, которые используют другой синтаксис и позволяют диспетчеризацию на основе получателя метода).
- В Джули нет классов. Вместо этого есть структуры (изменяемые или неизменяемые), содержащие данные, но не имеющие методов.
- Вызов метода экземпляра класса в Python (
x = MyClass(*args); x.f(y)
) соответствует вызову функции в Julia, например,x = MyType(args...); f(x, y)
. В общем, множественная диспетчеризация более гибкая и мощная, чем система классов Python. - Структуры Julia могут иметь ровно один абстрактный суперкласс, в то время как классы Python могут наследоваться от одного или нескольких (абстрактных или конкретных) суперклассов.
- Логическая структура программы на Julia (Пакеты и Модули) независима от структуры файлов, в то время как структура кода на Python определяется директориями (Пакеты) и файлами (Модули).
- В Julia идиоматично разбивать текст больших модулей на несколько файлов, не вводя новый модуль для каждого файла. Код собирается внутри одного модуля в основном файле с помощью
include
. В то время как эквивалент на Python (exec
) не является типичным для этого использования (он тихо перезапишет предыдущие определения), программы на Julia определяются как единое целое на уровнеmodule
с помощьюusing
илиimport
, которые будут выполняться только один раз при первом использовании – какinclude
в Python. Внутри этих модулей отдельные файлы, которые составляют этот модуль, загружаются с помощьюinclude
, перечисляя их один раз в нужном порядке. - Тернарный оператор
x > 0 ? 1 : -1
в Julia соответствует условному выражению в Python1 if x > 0 else -1
. - В Julia символ
@
относится к макросу, в то время как в Python он относится к декоратору. - Обработка исключений в Julia осуществляется с помощью
try
—catch
—finally
, вместоtry
—except
—finally
. В отличие от Python, не рекомендуется использовать обработку исключений как часть обычного рабочего процесса в Julia (по сравнению с Python, Julia быстрее в обычном управлении потоком, но медленнее при перехвате исключений). - В Julia циклы быстрые, нет необходимости писать "векторизованный" код по причинам производительности.
- Будьте осторожны с неконстантными глобальными переменными в Julia, особенно в тесных циклах. Поскольку вы можете писать код близкий к металлу в Julia (в отличие от Python), эффект глобальных переменных может быть драматичным (см. Performance Tips).
- В Julia округление и усечение являются явными.
int(3.7)
в Python должно бытьfloor(Int, 3.7)
илиInt(floor(3.7))
и отличается отround(Int, 3.7)
.floor(x)
иround(x)
сами по себе возвращают целочисленное значение того же типа, что иx
, а не всегда возвращаютInt
. - В Julia парсинг явный.
float("3.7")
в Python будетparse(Float64, "3.7")
в Julia. - В 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
не вводят новый локальный контекст в обоих языках.
Noteworthy differences from C/C++
- Массивы Julia индексируются квадратными скобками и могут иметь более одной размерности
A[i,j]
. Этот синтаксис не является просто синтаксическим сахаром для ссылки на указатель или адрес, как в C/C++. См. the manual entry about array construction. - В Julia индексация массивов, строк и т.д. начинается с 1, а не с 0.
- Массивы Julia не копируются при присвоении другой переменной. После
A = B
изменение элементовB
также изменитA
. Операторы обновления, такие как+=
, не работают на месте, они эквивалентныA = A + B
, что переназначает левую сторону на результат выражения правой стороны. - Массивы Julia имеют столбцовую основную структуру (упорядочены по Fortran), в то время как массивы C/C++ по умолчанию имеют строковую основную структуру. Для достижения оптимальной производительности при переборе массивов порядок циклов в Julia следует изменить относительно C/C++ (см. relevant section of Performance Tips).
- Значения 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, имеют тип, основанный на длине литерала, включая ведущие 0. Например,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. Эквивалентом 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)
). - Джулия не требует использования точек с запятой для завершения операторов. Результаты выражений не печатаются автоматически (за исключением интерактивного режима, т.е. REPL), и строки кода не обязательно должны заканчиваться точками с запятой.
println
или@printf
могут быть использованы для печати конкретного вывода. В REPL;
может быть использован для подавления вывода.;
также имеет другое значение внутри[ ]
, на что стоит обратить внимание.;
может быть использован для разделения выражений на одной строке, но не является строго необходимым во многих случаях и больше служит для удобочитаемости. - В Julia оператор
⊻
(xor
) выполняет побитовую операцию XOR, т.е.^
в C/C++. Также побитовые операторы не имеют такой же приоритет, как в C/C++, поэтому могут потребоваться скобки. - Julia's
^
является возведением в степень (pow), а не побитовым исключающим ИЛИ, как в C/C++ (используйте⊻
илиxor
в Julia) - Julia имеет два оператора сдвига вправо:
>>
и>>>
. Оператор>>
выполняет арифметический сдвиг, а>>>
всегда выполняет логический сдвиг, в отличие от C/C++, где значение оператора>>
зависит от типа сдвигаемого значения. ->
в Julia создает анонимную функцию, он не обращается к члену через указатель.- Julia не требует скобок при написании операторов
if
или цикловfor
/while
: используйтеfor i in [1, 2, 3]
вместоfor (int i=1; i <= 3; i++)
иif i == 1
вместоif (i == 1)
. - Julia не рассматривает числа
0
и1
как логические значения. Вы не можете написатьif (1)
в Julia, потому что операторыif
принимают только логические значения. Вместо этого вы можете написатьif true
,if Bool(1)
илиif 1==1
. - Джулия использует
end
для обозначения конца условных блоков, таких какif
, блоков циклов, таких какwhile
/for
, и функций. Вместо однострочногоif ( cond ) statement
, Джулия позволяет использовать конструкции вида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
. Если конец конструкции макроса может быть неясен, используйте форму, похожую на функцию. - Юлия имеет перечисляемый тип, выраженный с помощью макроса
@enum(name, value1, value2, ...)
. Например:@enum(Fruit, banana=1, apple, pear)
- По соглашению, функции, которые изменяют свои аргументы, имеют
!
в конце имени, напримерpush!
. - В C++ по умолчанию используется статическая диспетчеризация, т.е. вам нужно аннотировать функцию как виртуальную, чтобы иметь динамическую диспетчеризацию. С другой стороны, в Julia каждый метод является "виртуальным" (хотя это более общее понятие, поскольку методы диспетчеризуются по каждому типу аргумента, а не только по
this
, используя правило наиболее специфичной декларации).
Julia ⇔ C/C++: Namespaces
C/C++
namespace
s соответствуют примерно Juliamodule
ам.В Julia нет приватных глобальных переменных или полей. Все доступно публично через полностью квалифицированные пути (или относительные пути, если это необходимо).
using MyNamespace::myfun
(C++) соответствует примерноimport MyModule: myfun
(Julia).using namespace MyNamespace
(C++) примерно соответствуетusing MyModule
(Julia)- В Julia только
export
ированные символы становятся доступными для вызывающего модуля. - В C++ только элементы, найденные в включенных (публичных) заголовочных файлах, становятся доступными.
- В Julia только
Предостережение:
import
/using
ключевые слова (Julia) также загружают модули (см. ниже).Предостережение:
import
/using
(Julia) работает только на уровне глобальной области видимости (module
s)- В C++
using namespace X
работает в произвольных областях (например, в области функции).
- В C++
Julia ⇔ C/C++: Module loading
Когда вы думаете о C/C++ "библиотеке", вы, вероятно, ищете Julia "пакет".
- Предупреждение: Библиотеки 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++).
- В C/C++ вы
Репозитории пакетов на основе каталогов (Julia) могут быть доступны, если добавить пути репозиториев в массив
Base.LOAD_PATH
.- Пакеты из репозиториев на основе директорий не требуют инструмента
Pkg.add()
перед тем, как их загрузить с помощьюimport
илиusing
. Они просто доступны для проекта. - Репозитории пакетов на основе каталогов являются самым быстрым решением для разработки локальных библиотек "программных модулей".
- Пакеты из репозиториев на основе директорий не требуют инструмента
Julia ⇔ C/C++: Assembling modules
В C/C++ файлы
.c
/.cpp
компилируются и добавляются в библиотеку с помощью скриптов сборкиmake
.- В Julia,
import [PkgName]
/using [PkgName]
операторы загружают[PkgName].jl
, расположенный в подкаталоге пакета[PkgName]/src/
. - В свою очередь,
[PkgName].jl
обычно загружает связанные исходные файлы с помощью вызововinclude "[someotherfile].jl"
.
- В Julia,
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++: Module interface
C++ предоставляет интерфейсы с помощью "публичных"
.h
/.hpp
файлов, в то время какмодули
Julia помечают конкретные символы, которые предназначены для их пользователей, какpublic
илиexport
ed.- Часто модули Julia просто добавляют функциональность, создавая новые "методы" для существующих функций (например:
Base.push!
). - Разработчики пакетов Julia, следовательно, не могут полагаться на заголовочные файлы для документации интерфейса.
- Интерфейсы для пакетов Julia обычно описываются с помощью docstrings, README.md, статических веб-страниц и т. д.
- Часто модули Julia просто добавляют функциональность, создавая новые "методы" для существующих функций (например:
Некоторые разработчики выбирают не
export
все символы, необходимые для использования их пакета/модуля, но все же должны пометить неэкспортируемые символы, доступные пользователю, какpublic
.- Пользователи могут ожидать доступа к этим компонентам, квалифицируя функции/структуры/... именем пакета/модуля (например:
MyModule.run_this_task(...)
).
- Пользователи могут ожидать доступа к этим компонентам, квалифицируя функции/структуры/... именем пакета/модуля (например:
Julia ⇔ C/C++: Quick reference
Software Concept | Julia | C/C++ |
---|---|---|
unnamed scope | begin ... end | { ... } |
function scope | function x() ... end | int x() { ... } |
global scope | module MyMod ... end | namespace MyNS { ... } |
software module | A Julia "package" | .h /.hpp files<br>+compiled somelib.a |
assembling<br>software modules | SomePkg.jl : ...<br>import("subfile1.jl") <br>import("subfile2.jl") <br>... | $(AR) *.o ⇒ somelib.a |
import<br>software module | import SomePkg | #include <somelib> <br>+link in somelib.a |
module library | LOAD_PATH[] , *Git repository,<br>**custom package registry | more .h /.hpp files<br>+bigger compiled somebiglib.a |
- The Julia package manager supports registering multiple packages from a single Git repository.<br> * This allows users to house a library of related packages in a single repository.<br> ** Julia registries are primarily designed to provide versioning \& distribution of packages.<br> ** Custom package registries can be used to create a type of module library.
Noteworthy differences from Common Lisp
Джулия использует индексацию с 1 по умолчанию для массивов, и она также может обрабатывать произвольные index offsets.
Функции и переменные используют одно и то же пространство имен (“Lisp-1”).
Существует тип
Pair
, но он не предназначен для использования в качествеCOMMON-LISP:CONS
. В большинстве частей языка можно взаимозаменять различные итерируемые коллекции (например, распаковка, кортежи и т. д.).Tuple
наиболее близки к спискам Common Lisp для коротких коллекций гетерогенных элементов. ИспользуйтеNamedTuple
вместо alist. Для больших коллекций однородных типов следует использоватьArray
иDict
.Типичный рабочий процесс Julia для прототипирования также использует непрерывное манипулирование изображением, реализованное с помощью пакета Revise.jl.
Для производительности Julia предпочитает, чтобы операции имели type stability. В то время как Common Lisp абстрагируется от операций на уровне машины, Julia ближе к ним. Например:
Целочисленное деление с использованием
/
всегда возвращает результат с плавающей запятой, даже если вычисление является точным.//
всегда возвращает рациональный результат÷
всегда возвращает (усечённый) целочисленный результат
Bignums поддерживаются, но преобразование не является автоматическим; обычные целые числа overflow.
Комплексные числа поддерживаются, но для получения комплексных результатов, you need complex inputs.
Существует несколько типов Complex и Rational с различными компонентными типами.
Модули (пространства имен) могут быть иерархическими.
import
иusing
имеют двойную роль: они загружают код и делают его доступным в пространстве имен.import
только для имени модуля возможен (приблизительно эквивалентенASDF:LOAD-OP
). Имена слотов не нужно экспортировать отдельно. Глобальные переменные не могут быть присвоены из вне модуля (за исключениемeval(mod, :(var = val))
как обходного пути).Макросы начинаются с
@
, и они не так бесшовно интегрированы в язык, как в Common Lisp; следовательно, использование макросов не так широко распространено, как в последнем. Язык поддерживает форму гигиены для macros. Из-за различного синтаксиса на поверхности нет эквивалентаCOMMON-LISP:&BODY
.Все функции являются универсальными и используют множественную диспетчеризацию. Списки аргументов не обязаны следовать одной и той же структуре, что приводит к мощной идиоме (см.
do
). Необязательные и именованные аргументы обрабатываются по-разному. Неоднозначности методов не разрешаются так, как в системе объектов Common Lisp, что требует определения более специфичного метода для пересечения.Символы не принадлежат никакому пакету и не содержат никаких значений per se.
M.var
оценивает символvar
в модулеM
.Стиль функционального программирования полностью поддерживается языком, включая замыкания, но не всегда является идиоматическим решением для Julia. Некоторые workarounds могут быть необходимы для производительности при изменении захваченных переменных.