Почему я делаю ставку на Julia

в 17:48, , рубрики: fortran, Julia, python, Программирование, производительность

imageСовсем о Julia не говорим тут. Один пост двухлетней давности от Ализара, и всё. Исправляем ситуацию.

Большинство языков программирования сталкиваются с одной и той же проблемой — их создатели без ума от вещей, которые меня практически не волнуют: безопасность, системы типов, гомоиконность и так далее. Всё это очень круто, не спорю, но когда я вожусь по вечерам над своим очередным проектом, мне важна только его работоспособность и производительность. Код — это всего лишь средство для достижения некоторой цели, а его «выразительность» для меня важна так же, как и «выразительность» какого-нибудь каталитического конвертера.

Такой подход к делу некоторые презрительно называют ковбойским программированием. Но мне кажется, что это не самый правильный образ — ковбой вынужден периодически устраивать привалы из-за физических ограничений своего коня. Давайте лучше представим одержимого учёного, эдакого профессора, который неделями пропадает в лаборатории, а потом выходит оттуда изнурённый, с затуманенным взором, со своим новым хитроумным изобретением, которое разваливается при первом же запуске.

Я обычно работаю таким образом: сначала пишу прототип на одном языке, после переписываю критические секции на другом языке, а если мне нужно, чтобы программа летала, я использую третий язык. Такой подход довольно распространён. Для прототипирования многие используют Python, Ruby, R и т.п. Как только всё заработало, некоторые куски кода переписываются на C или C++. Если и этого недостаточно, некоторые циклы переписываются на ассемблере, CUDA или OpenCL.

Но в этом случае мы сталкиваемся со сложными препятствиями. Мало того, что нужно знать три языка, так ещё и приходится постоянно переключаться между разными уровнями абстракции. А в реальной жизни ещё приходится писать связующий код — переключаться между разными файлами, редакторами и дебаггерами.

Некоторое время назад я узнал о Julia, язык мне сразу показался крутым, но особой необходимости в нём я не ощутил. Julia — высокопроизводительный динамический язык. Заманчиво, конечно, но я уже и так угробил кучу времени, чтобы засунуть движок от бэхи в свою шоху — зачем мне ещё что-то? Кроме того, есть куча платформ, которые обещают производительность на уровне C: Java HotSpot, PyPy, asm.js и другие.

И только потом я понял, что отличает Julia от других языков. Julia разрушает барьер между высокоуровневым и ассемблерным кодом. Julia не просто позволяет писать код, который работает так же быстро, как код на C, но и даёт возможность посмотреть на LLVM-представление функций и их сгенерированный ассемблерный код. И всё это прямо в интерактивной среде.

emiller ~/Code/julia (master) ./julia
               _
   _       _ _(_)_     |  A fresh approach to technical computing
  (_)     | (_) (_)    |  Documentation: http://docs.julialang.org
   _ _   _| |_  __ _   |  Type "help()" to list help topics
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 0.3.0-prerelease+261 (2013-11-30 12:55 UTC)
 _/ |__'_|_|_|__'_|  |  Commit 97b5983 (0 days old master)
|__/                   |  x86_64-apple-darwin12.5.0

julia> f(x) = x * x
f (generic function with 1 method)

julia> f(2.0)
4.0

julia> code_llvm(f, (Float64,))

define double @julia_f662(double) {
top:
  %1 = fmul double %0, %0, !dbg !3553
  ret double %1, !dbg !3553
}

julia> code_native(f, (Float64,))
     .section        __TEXT,__text,regular,pure_instructions
Filename: none
Source line: 1
        push    RBP
        mov     RBP, RSP
Source line: 1
        vmulsd  XMM0, XMM0, XMM0
        pop     RBP
        ret

Вот так вот. Мы можем написать однострочную функцию и тут же изучить её оптимизированный ассемблерный код.

В общем, можете забыть о системе типов Julia, мультиметодах и прочей гомоиконности. Это всё, конечно, занимательно, но настоящий козырь Julia в том, что мы можем начать с прототипа, а закончить оптимизацией SIMD-инструкциями не покидая одного языка.

В общем, это основная причина, по которой я делаю ставку на Julia. Мне не терпится сделать такое сравнение: этот язык сделает для технических расчётов то, что Node.js делает для веб-разработки — объединяет разные группы программистов одним языком. Если для Node.js этими группами являются фронтенд- и бекенд-разработчики, то для Julia — специалисты в определенных областях знаний и безумные оптимизаторы. Это большое достижение.

На данный момент единственный недостаток языка — дефицит библиотек. Но и он компенсируется лёгкостью взаимодействия с библиотеками на C. В отличие от интерфейсов для взаимодействия других языков, здесь можно вызвать функцию C, не написав ни строчки на C, поэтому, мне кажется, количество библиотек скоро начнёт свой быстрый рост. Если говорить о собственном опыте, то мне удалось без какого-либо дополнительного кода на C воспользоваться 5 тысячами строк кода на C в своих 150 строках кода на Julia.

Если вам приходится поддерживать код из смеси Python, C, C++, Fortran и R или вы просто, как и я, одержимы производительностью, то настоятельно вам рекомендую скачать Julia и дать ей шанс. Если вы не уверены, стоит ли усложнять свою жить ещё одним языком программирования, то просто осознайте, что этот инструмент в итоге позволит вам сократить количество языков в ваших проектах.

В конце концов, если забыть на минуту о производительности, Julia — невероятно красивый язык. Я не из гиков, но я почти ни разу не столкнулся с какими-нибудь трудностями, пока изучал язык. И сейчас Julia для меня один из трёх предпочтительных языков.

В конце концов, вокруг Julia собралось активное сообщество, всегда готовое прийти на помощь. Особенно меня радует, что немалую его часть составляют умные и дружелюбные математики и представители других наук. Думаю, так получилось именно потому, что Julia была создана не гиками, а студентами точных наук из MIT, которым нужен быстрый и удобный язык на замену C и Фортрану. И создан он был не для того, чтобы быть красивым (хоть и получился таким). Создан он был для быстрого получения ответов. А это, я считаю, и является сутью всей нашей компьютерной науки.

Конец поста Эвана Миллера.

Пост, похожий по настроению, появился сегодня на Wired.

Julia вкратце

Чтобы два раза не вставать, представляю перевод документа Learn Julia in minutes.

# Однострочные комментарии начинаются со знака решётки.

####################################################
## 1. Примитивные типы данных и операторы
####################################################

# Всё в Julia — выражение.

# Простые численные типы
3 #=> 3 (Int64)
3.2 #=> 3.2 (Float64)
2 + 1im #=> 2 + 1im (Complex{Int64})
2//3 #=> 2//3 (Rational{Int64})

# Доступны все привычные инфиксные операторы
1 + 1 #=> 2
8 - 1 #=> 7
10 * 2 #=> 20
35 / 5 #=> 7.0
5 / 2 #=> 2.5 # деление Int на Int всегда возвращает Float
div(5, 2) #=> 2 # для округления к нулю используется div
5  35 #=> 7.0
2 ^ 2 #=> 4 # возведение в степень
12 % 10 #=> 2

# С помощью скобок можно изменить приоритет операций
(1 + 3) * 2 #=> 8

# Побитовые операторы
~2 #=> -3   # НЕ (NOT)
3 & 5 #=> 1 # И (AND)
2 | 4 #=> 6 # ИЛИ (OR)
2 $ 4 #=> 6 # сложение по модулю 2 (XOR)
2 >>> 1 #=> 1 # логический сдвиг вправо
2 >> 1  #=> 1 # арифметический сдвиг вправо
2 << 1  #=> 4 # логический/арифметический сдвиг влево

# Функция bits возвращает бинарное представление числа
bits(12345)
#=> "0000000000000000000000000000000000000000000000000011000000111001"
bits(12345.0)
#=> "0100000011001000000111001000000000000000000000000000000000000000"

# Логические значения являются примитивами
true
false

# Булевы операторы
!true #=> false
!false #=> true
1 == 1 #=> true
2 == 1 #=> false
1 != 1 #=> false
2 != 1 #=> true
1 < 10 #=> true
1 > 10 #=> false
2 <= 2 #=> true
2 >= 2 #=> true
# Сравнения можно объединять цепочкой
1 < 2 < 3 #=> true
2 < 3 < 2 #=> false

# Строки объявляются с помощью двойных кавычек — "
"This is a string."

# Символьные литералы создаются с помощью одинарных кавычек — '
'a'

# Строки индексируются как массивы символов
"This is a string"[1] #=> 'T' # Индексы начинаются с единицы
# Индексирование не всегда правильно работает для UTF8-строк,
# поэтому рекомендуется использовать итерирование (map, for-циклы и т.п.).

# Для строковой интерполяции используется знак доллара ($):
"2 + 2 = $(2 + 2)" #=> "2 + 2 = 4"
# В скобках можно использовать любое выражение языка.

# Другой способ форматирования строк — макрос printf
@printf "%d is less than %f" 4.5 5.3 # 5 is less than 5.300000

####################################################
## 2. Переменные и коллекции
####################################################

# Вывод
println("I'm Julia. Nice to meet you!")

# Переменные инициализируются без предварительного объявления
some_var = 5 #=> 5
some_var #=> 5

# Попытка доступа к переменной до инициализации вызывает ошибку
try
    some_other_var #=> ERROR: some_other_var not defined
catch e
    println(e)
end

# Имена переменных начинаются с букв.
# После первого символа можно использовать буквы, цифры, 
# символы подчёркивания и восклицательные знаки.
SomeOtherVar123! = 6 #=> 6

# Допустимо использование unicode-символов
☃ = 8 #=> 8
# Это особенно удобно для математических обозначений
2 * π #=> 6.283185307179586

# Рекомендации по именованию:
# * имена переменных в нижнем регистре, слова разделяются символом 
#   подчёркивания ('_');
#
# * для имён типов используется CamelCase;
#
# * имена функций и макросов в нижнем регистре
#   без разделения слов символом подчёркивания;
#
# * имя функции, изменяющей переданные ей аргументы (in-place function),
#   оканчивается восклицательным знаком.

# Массив хранит последовательность значений, индексируемых с единицы до n:
a = Int64[] #=> пустой массив Int64-элементов

# Одномерный массив объявляется разделёнными запятой значениями.
b = [4, 5, 6] #=> массив из трёх Int64-элементов: [4, 5, 6]
b[1] #=> 4
b[end] #=> 6

# Строки двумерного массива разделяются точкой с запятой.
# Элементы строк разделяются пробелами.
matrix = [1 2; 3 4] #=> 2x2 Int64 Array: [1 2; 3 4]

# push! и append! добавляют в список новые элементы
push!(a,1)     #=> [1]
push!(a,2)     #=> [1,2]
push!(a,4)     #=> [1,2,4]
push!(a,3)     #=> [1,2,4,3]
append!(a,b) #=> [1,2,4,3,4,5,6]

# pop! удаляет из списка последний элемент
pop!(b)        #=> возвращает 6; массив b снова равен [4,5]

# Вернём 6 обратно
push!(b,6)   # b снова [4,5,6].

a[1] #=> 1 # индексы начинаются с единицы!

# Последний элемент можно получить с помощью end
a[end] #=> 6

# Операции сдвига
shift!(a) #=> 1 and a is now [2,4,3,4,5,6]
unshift!(a,7) #=> [7,2,4,3,4,5,6]

# Восклицательный знак на конце названия функции означает,
# что функция изменяет переданные ей аргументы.
arr = [5,4,6] #=> массив из 3 Int64-элементов: [5,4,6]
sort(arr) #=> [4,5,6]; но arr равен [5,4,6]
sort!(arr) #=> [4,5,6]; а теперь arr — [4,5,6]

# Попытка доступа за пределами массива выбрасывает BoundsError
try
    a[0] #=> ERROR: BoundsError() in getindex at array.jl:270
    a[end+1] #=> ERROR: BoundsError() in getindex at array.jl:270
catch e
    println(e)
end

# Вывод ошибок содержит строку и файл, где произошла ошибка,
# даже если это случилось в стандартной библиотеке.
# Если вы собрали Julia из исходных кодов, 
# то найти эти файлы можно в директории base.

# Создавать массивы можно из последовательности
a = [1:5] #=> массив из 5 Int64-элементов: [1,2,3,4,5]

# Срезы
a[1:3] #=> [1, 2, 3]
a[2:] #=> [2, 3, 4, 5]
a[2:end] #=> [2, 3, 4, 5]

# splice! удаляет элемент из массива
# Remove elements from an array by index with splice!
arr = [3,4,5]
splice!(arr,2) #=> 4 ; arr теперь равен [3,5]

# append! объединяет списки
b = [1,2,3]
append!(a,b) # теперь a равен [1, 2, 3, 4, 5, 1, 2, 3]

# Проверка на вхождение
in(1, a) #=> true

# Длина списка
length(a) #=> 8

# Кортеж — неизменяемая структура.
tup = (1, 2, 3) #=> (1,2,3) # кортеж (Int64,Int64,Int64).
tup[1] #=> 1
try:
    tup[1] = 3 #=> ERROR: no method setindex!((Int64,Int64,Int64),Int64,Int64)
catch e
    println(e)
end

# Многие функции над списками работают и для кортежей
length(tup) #=> 3
tup[1:2] #=> (1,2)
in(2, tup) #=> true

# Кортежи можно распаковывать в переменные
a, b, c = (1, 2, 3) #=> (1,2,3)  # a = 1, b = 2 и c = 3

# Скобки из предыдущего примера можно опустить
d, e, f = 4, 5, 6 #=> (4,5,6)

# Кортеж из одного элемента не равен значению этого элемента
(1,) == 1 #=> false
(1) == 1 #=> true

# Обмен значений
e, d = d, e  #=> (5,4) # d = 5, e = 4


# Словари содержат ассоциативные массивы
empty_dict = Dict() #=> Dict{Any,Any}()

# Для создания словаря можно использовать литерал
filled_dict = ["one"=> 1, "two"=> 2, "three"=> 3]
# => Dict{ASCIIString,Int64}

# Значения ищутся по ключу с помощью оператора []
filled_dict["one"] #=> 1

# Получить все ключи
keys(filled_dict)
#=> KeyIterator{Dict{ASCIIString,Int64}}(["three"=>3,"one"=>1,"two"=>2])
# Заметьте, словарь не запоминает порядок, в котором добавляются ключи.

# Получить все значения.
values(filled_dict)
#=> ValueIterator{Dict{ASCIIString,Int64}}(["three"=>3,"one"=>1,"two"=>2])
# То же касается и порядка значений.

# Проверка вхождения ключа в словарь
in(("one", 1), filled_dict) #=> true
in(("two", 3), filled_dict) #=> false
haskey(filled_dict, "one") #=> true
haskey(filled_dict, 1) #=> false

# Попытка обратиться к несуществующему ключу выбросит ошибку
try
    filled_dict["four"] #=> ERROR: key not found: four in getindex at dict.jl:489
catch e
    println(e)
end

# Используйте метод get со значением по умолчанию, чтобы избежать этой ошибки
# get(dictionary,key,default_value)
get(filled_dict,"one",4) #=> 1
get(filled_dict,"four",4) #=> 4

# Для коллекций неотсортированных уникальных элементов используйте Set
empty_set = Set() #=> Set{Any}()
# Инициализация множества
filled_set = Set(1,2,2,3,4) #=> Set{Int64}(1,2,3,4)

# Добавление элементов
push!(filled_set,5) #=> Set{Int64}(5,4,2,3,1)

# Проверка вхождения элементов во множество
in(2, filled_set) #=> true
in(10, filled_set) #=> false

# Функции для получения пересечения, объединения и разницы.
other_set = Set(3, 4, 5, 6) #=> Set{Int64}(6,4,5,3)
intersect(filled_set, other_set) #=> Set{Int64}(3,4,5)
union(filled_set, other_set) #=> Set{Int64}(1,2,3,4,5,6)
setdiff(Set(1,2,3,4),Set(2,3,5)) #=> Set{Int64}(1,4)


####################################################
## 3. Поток управления
####################################################

# Создадим переменную
some_var = 5

# Выражение if. Отступы не имеют значения.
if some_var > 10
    println("some_var is totally bigger than 10.")
elseif some_var < 10    # Необязательная ветка elseif.
    println("some_var is smaller than 10.")
else                    # else-ветка также опциональна.
    println("some_var is indeed 10.")
end
#=> prints "some var is smaller than 10"


# Цикл for проходит по итерируемым объектам
# Примеры итерируемых типов: Range, Array, Set, Dict и String.
for animal=["dog", "cat", "mouse"]
    println("$animal is a mammal")
    # Для вставки значения переменной или выражения в строку используется $
end
# Выведет:
#    dog is a mammal
#    cat is a mammal
#    mouse is a mammal

# Другой вариант записи.
for animal in ["dog", "cat", "mouse"]
    println("$animal is a mammal")
end
# Выведет:
#    dog is a mammal
#    cat is a mammal
#    mouse is a mammal

for a in ["dog"=>"mammal","cat"=>"mammal","mouse"=>"mammal"]
    println("$(a[1]) is a $(a[2])")
end
# Выведет:
#    dog is a mammal
#    cat is a mammal
#    mouse is a mammal

for (k,v) in ["dog"=>"mammal","cat"=>"mammal","mouse"=>"mammal"]
    println("$k is a $v")
end
# Выведет:
#    dog is a mammal
#    cat is a mammal
#    mouse is a mammal

# Цикл while выполняется до тех пор, пока верно условие
x = 0
while x < 4
    println(x)
    x += 1  # Короткая запись x = x + 1
end
# Выведет:
#   0
#   1
#   2
#   3

# Обработка исключений
try
   error("help")
catch e
   println("caught it $e")
end
#=> caught it ErrorException("help")


####################################################
## 4. Функции
####################################################

# Для определения новой функции используется ключевое слово 'function'
#function имя(аргументы)
#  тело...
#end
function add(x, y)
    println("x is $x and y is $y")

    # Функция возвращает значение последнего выражения
    x + y
end

add(5, 6) #=> Вернёт 11, напечатав "x is 5 and y is 6"

# Функция может принимать переменное количество позиционных аргументов.
function varargs(args...)
    return args
    # для возвращения из функции в любом месте используется 'return'
end
#=> varargs (generic function with 1 method)

varargs(1,2,3) #=> (1,2,3)

# Многоточие (...) — это splat.
# Мы только что воспользовались им в определении функции.
# Также его можно использовать при вызове функции,
# где он преобразует содержимое массива или кортежа в список аргументов.
Set([1,2,3])    #=> Set{Array{Int64,1}}([1,2,3]) # формирует множество массивов
Set([1,2,3]...) #=> Set{Int64}(1,2,3) # эквивалентно Set(1,2,3)

x = (1,2,3)     #=> (1,2,3)
Set(x)          #=> Set{(Int64,Int64,Int64)}((1,2,3)) # множество кортежей
Set(x...)       #=> Set{Int64}(2,3,1)


# Опциональные позиционные аргументы
function defaults(a,b,x=5,y=6)
    return "$a $b and $x $y"
end

defaults('h','g') #=> "h g and 5 6"
defaults('h','g','j') #=> "h g and j 6"
defaults('h','g','j','k') #=> "h g and j k"
try
    defaults('h') #=> ERROR: no method defaults(Char,)
    defaults() #=> ERROR: no methods defaults()
catch e
    println(e)
end

# Именованные аргументы
function keyword_args(;k1=4,name2="hello") # обратите внимание на ;
    return ["k1"=>k1,"name2"=>name2]
end

keyword_args(name2="ness") #=> ["name2"=>"ness","k1"=>4]
keyword_args(k1="mine") #=> ["k1"=>"mine","name2"=>"hello"]
keyword_args() #=> ["name2"=>"hello","k2"=>4]

# В одной функции можно совмещать все виды аргументов
function all_the_args(normal_arg, optional_positional_arg=2; keyword_arg="foo")
    println("normal arg: $normal_arg")
    println("optional arg: $optional_positional_arg")
    println("keyword arg: $keyword_arg")
end

all_the_args(1, 3, keyword_arg=4)
# Выведет:
#   normal arg: 1
#   optional arg: 3
#   keyword arg: 4

# Функции в Julia первого класса 
function create_adder(x)
    adder = function (y)
        return x + y
    end
    return adder
end

# Анонимная функция
(x -> x > 2)(3) #=> true

# Эта функция идентичная предыдущей версии create_adder
function create_adder(x)
    y -> x + y
end

# Если есть желание, можно воспользоваться полным вариантом
function create_adder(x)
    function adder(y)
        x + y
    end
    adder
end

add_10 = create_adder(10)
add_10(3) #=> 13


# Встроенные функции высшего порядка
map(add_10, [1,2,3]) #=> [11, 12, 13]
filter(x -> x > 5, [3, 4, 5, 6, 7]) #=> [6, 7]

# Списковые сборки
[add_10(i) for i=[1, 2, 3]] #=> [11, 12, 13]
[add_10(i) for i in [1, 2, 3]] #=> [11, 12, 13]

####################################################
## 5. Типы
####################################################

# Julia has a type system.
# Каждое значение имеет тип, но переменные не определяют тип значения.
# Функция `typeof` возвращает тип значения.
typeof(5) #=> Int64

# Types are first-class values
# Типы являются значениями первого класса
typeof(Int64) #=> DataType
typeof(DataType) #=> DataType
# Тип DataType представляет типы, включая себя самого.

# Типы используются в качестве документации, для оптимизации и организации.
# Статически типы не проверяются.

# Пользователь может определять свои типы
# Типы похожи на структуры в других языках
# Новые типы определяются с помощью ключевого слова `type`

# type Name
#   field::OptionalType
#   ...
# end
type Tiger
  taillength::Float64
  coatcolor # отсутствие типа равносильно `::Any`
end

# Аргументы конструктора по умолчанию — свойства типа
# в порядке их определения.
tigger = Tiger(3.5,"orange") #=> Tiger(3.5,"orange")

# Тип объекта по сути является конструктором значений такого типа
sherekhan = typeof(tigger)(5.6,"fire") #=> Tiger(5.6,"fire")

# Эти типы, похожие на структуры, называются конкретными.
# Можно создавать объекты таких типов, но не их подтипы.
# Другой вид типов — абстрактные типы.

# abstract Name
abstract Cat # просто имя и точка в иерархии типов

# Объекты абстрактных типов создавать нельзя, 
# но зато от них можно наследовать подтипы.
# Например, Number — это абстрактный тип.
subtypes(Number) #=> 6 элементов в массиве Array{Any,1}:
                 #     Complex{Float16}
                 #     Complex{Float32}
                 #     Complex{Float64}
                 #     Complex{T<:Real}
                 #     ImaginaryUnit
                 #     Real
subtypes(Cat) #=> пустой массив Array{Any,1}

# У всех типов есть супертип. Для его определения есть функция `super`.
typeof(5) #=> Int64
super(Int64) #=> Signed
super(Signed) #=> Real
super(Real) #=> Number
super(Number) #=> Any
super(super(Signed)) #=> Number
super(Any) #=> Any
# Все эти типы, за исключением Int64, абстрактные.

# Для создания подтипа используется оператор <:
type Lion <: Cat # Lion — это подтип Cat
  mane_color
  roar::String
end

# У типа может быть несколько конструкторов.
# Для создания нового определите функцию с именем, как у типа,
# и вызовите имеющийся конструктор.
Lion(roar::String) = Lion("green",roar)
# Мы создали внешний (т.к. он находится вне определения типа) конструктор.

type Panther <: Cat # Panther — это тоже подтип Cat
  eye_color

  # Определим свой конструктор вместо конструктора по умолчанию
  Panther() = new("green")
end
# Использование внутренних конструкторов позволяет
# определять, как будут создаваться объекты типов.
# Но по возможности стоит пользоваться внешними конструкторами.

####################################################
## 6. Мультиметоды
####################################################

# Все именованные функции являются generic-функциями,
# т.е. все они состоят из разных методов.
# Каждый конструктор типа Lion — это метод generic-функции Lion.

# Приведём пример без использования конструкторов, создадим функцию meow

# Определения Lion, Panther и Tiger
function meow(animal::Lion)
  animal.roar # доступ к свойству типа через точку
end

function meow(animal::Panther)
  "grrr"
end

function meow(animal::Tiger)
  "rawwwr"
end

# Проверка
meow(tigger) #=> "rawwr"
meow(Lion("brown","ROAAR")) #=> "ROAAR"
meow(Panther()) #=> "grrr"

# Вспомним иерархию типов
issubtype(Tiger,Cat) #=> false
issubtype(Lion,Cat) #=> true
issubtype(Panther,Cat) #=> true

# Определим функцию, принимающую на вход объекты типа Cat
function pet_cat(cat::Cat)
  println("The cat says $(meow(cat))")
end

pet_cat(Lion("42")) #=> выведет "The cat says 42"
try
    pet_cat(tigger) #=> ERROR: no method pet_cat(Tiger,)
catch e
    println(e)
end

# В объектно-ориентированных языках распространена одиночная диспетчеризация —
# подходящий метод выбирается на основе типа первого аргумента.
# В Julia все аргументы участвуют в выборе нужного метода.

# Чтобы понять разницу, определим функцию с несколькими аргументами.
function fight(t::Tiger,c::Cat)
  println("The $(t.coatcolor) tiger wins!")
end
#=> fight (generic function with 1 method)

fight(tigger,Panther()) #=> выведет The orange tiger wins!
fight(tigger,Lion("ROAR")) #=> выведет The orange tiger wins!

# Переопределим поведение функции, если Cat-объект является Lion-объектом
fight(t::Tiger,l::Lion) = println("The $(l.mane_color)-maned lion wins!")
#=> fight (generic function with 2 methods)

fight(tigger,Panther()) #=> выведет The orange tiger wins!
fight(tigger,Lion("ROAR")) #=> выведет The green-maned lion wins!

# Драться можно не только с тиграми!
fight(l::Lion,c::Cat) = println("The victorious cat says $(meow(c))")
#=> fight (generic function with 3 methods)

fight(Lion("balooga!"),Panther()) #=> выведет The victorious cat says grrr
try
  fight(Panther(),Lion("RAWR")) #=> ERROR: no method fight(Panther,Lion)
catch
end

# Вообще, пускай кошачьи могут первыми проявлять агрессию
fight(c::Cat,l::Lion) = println("The cat beats the Lion")
#=> Warning: New definition
#    fight(Cat,Lion) at none:1
# is ambiguous with
#    fight(Lion,Cat) at none:2.
# Make sure
#    fight(Lion,Lion)
# is defined first.
#fight (generic function with 4 methods)

# Предупреждение говорит, что неясно, какой из методов вызывать:
fight(Lion("RAR"),Lion("brown","rarrr")) #=> выведет The victorious cat says rarrr
# Результат может оказаться разным в разных версиях Julia

fight(l::Lion,l2::Lion) = println("The lions come to a tie")
fight(Lion("RAR"),Lion("brown","rarrr")) #=> выведет The lions come to a tie

Что дальше?

Читать документацию! А помощи искать в списке рассылки.

Автор: skovorodkin

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js