Инициализация массива для хранения объектов - PullRequest
3 голосов
/ 04 марта 2020

В Джулии, как инициализировать массив для хранения изменяемых структур? Например:

mutable struct Foo
    func # Some function
end

function Bar(param1, param2)
    function nestedFunc(param3)
        return param3
    end

    return Foo(nestedFunc)
end

# Initializing with 0s will throw an error when 
# I attempt to store the struct returned in the Bar call below
arr = zeros(5, 5)    
for i in 1:5
    for j in 1:5
        arr[i, j] = Bar(i, j)
    end
end

Я также пытался инициализировать массив типа Any как arr = Array{Any, (5, 5)}, но, опять же, при установке индекса я получаю что-то вроде ERROR: LoadError: MethodError: no method matching setindex!

Как я могу это сделать, и каково обоснование такого поведения, которое я вижу? В Python я могу произвольно инициализировать список и устанавливать практически все, что мне нужно, в пределах определенного индекса.

Ответы [ 2 ]

3 голосов
/ 04 марта 2020

Вы можете делать, что хотите, если вы инициализируете свой массив следующим образом:

arr = Array{Foo,2}(undef,5,5)

В этом примере вы создаете массив, в котором каждый элемент может иметь тип Foo, и он имеет два измерения. Параметры, передаваемые этому «конструктору», имеют значение undef, что говорит о том, что не все элементы инициализированы, и о размере двух измерений. Вы можете проверить раздел Создание и инициализация раздела Arrays руководства Julia .

Если вы ищете гибкость списка python, в который вы можете помещать объекты любого типа, вы можете сделать

arr = Array{Any,2}(undef,5,5)

В этом случае Any указывает, что элементы массива могут быть любого типа.

Однако вы должны знать, что это, вероятно, плохой метод, если вам нужна производительность, поскольку наличие массивов с элементами типа «Любой» препятствует созданию Джулией эффективный код для ваших массивов.

Почему ваш первый пример не работал

Вы создавали массив с элементами типа Float64, и вы пытались назначить функцию этим элементам , Вы не можете изменить тип массива на лету. Наличие определенного типа для элементов позволяет julia иметь эффективный способ сохранения вещей в памяти, а также позволяет компилятору использовать быстрые функции при выполнении операций с массивами. Посмотрите на раздел Types руководства Julia

Почему не работает второй пример

Это потому, что вы не использовали правильную форму для инициализатора. Я показал правильную форму.

PS Вы уверены, что можете сделать это с numpy массивами?

Я только что попробовал

import numpy as np
a = np.zeros(5)
a[0] = 'hello'

и получил ошибка типа, похожая на ту, что вы видите у Юлии:

ValueError: could not convert string to float: 'hola'
1 голос
/ 04 марта 2020

Совет: никогда не используйте нетипизированные контейнеры, за исключением случаев, когда это действительно необходимо (обычно это почти никогда не требуется)

Вот правильный способ сделать это (я сократил bar для удобства чтения, имена функций должны начинаться с маленькими буквами):


mutable struct Foo
    func::Function
end

function bar(param1, param2)
    nestedFunc(param3) = param3
    return Foo(nestedFunc)
end

А теперь

julia> myarr = [bar(i,j) for i in 1:3, j in 1:4]
3×4 Array{Foo,2}:
 Foo(nestedFunc)  Foo(nestedFunc)  Foo(nestedFunc)  Foo(nestedFunc)
 Foo(nestedFunc)  Foo(nestedFunc)  Foo(nestedFunc)  Foo(nestedFunc)
 Foo(nestedFunc)  Foo(nestedFunc)  Foo(nestedFunc)  Foo(nestedFunc)

Другой вариант (обратите внимание, что круглые скобки () создают генератор, а точка . векторизует оператор присваивания):

myarr2 =Matrix{Foo}(undef,3,4)
myarr2 .= (bar(i,j)  for i=1:3, j=1:4)

И самый длинный вариант (обратите внимание, что контейнер набирается):

myarr3 =Matrix{Foo}(undef,3,4)
for i=1:3, j=1:4
    myarr3 = bar(i,j)
end
...