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], соответствующее выражение Julia a[5] = 7 вызывает ошибку, если длина a меньше 5 или если это выражение является первым использованием идентификатора a. Julia имеет push! и append!, которые увеличивают Vectors гораздо эффективнее, чем MATLAB's a(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 выполняет сопряженное транспонирование; в MATLAB adjoint предоставляет "адъюгат" или классический адъюгат, который является транспонированной матрицей кофакторов.

  • В 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].
  • Как и многие языки, 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) одинаковы. В Джулии их нельзя использовать взаимозаменяемо.

  • Julia's diag и diagm не похожи на R.

  • Юлия не может присваивать значения результатам вызовов функций с левой стороны операции присваивания: вы не можете написать 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]], относится к подматрице, которая содержит пересечения первых и вторых строк с первыми и третьими столбцами. В 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 имеют столбцовую порядок (упорядочены по 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 соответствует условному выражению в Python 1 if x > 0 else -1.
  • В Julia символ @ относится к макросу, в то время как в Python он относится к декоратору.
  • Обработка исключений в Julia осуществляется с помощью trycatchfinally, вместо tryexceptfinally. В отличие от 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(""). Возможно, это удивительно, но в Python if "False" и bool("False") оба оцениваются как True (потому что "False" — это непустая строка); в Julia parse(Bool, "false") возвращает false.
  • В Julia новый локальный контекст вводится большинством блоков кода, включая циклы и trycatchfinally. Обратите внимание, что генераторы (списки, генераторы и т. д.) вводят новый локальный контекст как в 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++ namespaces соответствуют примерно Julia moduleам.

  • В 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) работает только на уровне глобальной области видимости (modules)

    • В C++ using namespace X работает в произвольных областях (например, в области функции).

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++).
  • Репозитории пакетов на основе каталогов (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".
  • 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 или exported.

    • Часто модули Julia просто добавляют функциональность, создавая новые "методы" для существующих функций (например: Base.push!).
    • Разработчики пакетов Julia, следовательно, не могут полагаться на заголовочные файлы для документации интерфейса.
    • Интерфейсы для пакетов Julia обычно описываются с помощью docstrings, README.md, статических веб-страниц и т. д.
  • Некоторые разработчики выбирают не export все символы, необходимые для использования их пакета/модуля, но все же должны пометить неэкспортируемые символы, доступные пользователю, как public.

    • Пользователи могут ожидать доступа к этим компонентам, квалифицируя функции/структуры/... именем пакета/модуля (например: MyModule.run_this_task(...)).

Julia ⇔ C/C++: Quick reference

Software ConceptJuliaC/C++
unnamed scopebegin ... end{ ... }
function scopefunction x() ... endint x() { ... }
global scopemodule MyMod ... endnamespace MyNS { ... }
software moduleA Julia "package".h/.hpp files<br>+compiled somelib.a
assembling<br>software modulesSomePkg.jl: ...<br>import("subfile1.jl")<br>import("subfile2.jl")<br>...$(AR) *.o &rArr; somelib.a
import<br>software moduleimport SomePkg#include <somelib><br>+link in somelib.a
module libraryLOAD_PATH[], *Git repository,<br>**custom package registrymore .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 могут быть необходимы для производительности при изменении захваченных переменных.