Конструкторы в Julia: инициализация именованных полей на основе входного значения других именованных полей

Представьте конструктор, который принимает два аргумента и инициализирует 3 именованных поля, используя значения двух аргументов. Что-то вроде этого:

type test1
   a
   b
   c
   test1(a,b) = new(a,b,a/b)
end

Это нормально работает, но что, если значение c не такое простое выражение? Что, если он выйдет за одну-две строчки? Или сложный список? Вставка выражения для c непосредственно в new() является громоздкой и затрудняет чтение кода (IMO). Я бы лучше сделал что-то вроде этого:

type test1
   a
   b
   c = a/b
   test1(a,b) = new(a,b,c)
end

но a и b, по-видимому, не определены до вызова test1(a,b), поэтому это не работает. Возможно, я просто ищу синтаксический сахар. В любом случае, хотелось бы лучше понять, когда станут известны значения аргументов конструктора и можно ли их использовать до вызова new().

Есть ли лучший способ (лучше, чем в первом примере) сделать то, что я пытаюсь сделать во втором примере?

(Я думаю, что следующий вопрос и ответы на него достаточно связаны, чтобы быть полезными, но я все еще слишком новичок в Джулии Создание конструктора не по умолчанию в Julia)

Отредактировано: Рискуя быть слишком конкретным, я подумал, что включу фактический вариант использования, в котором возник этот вопрос. Я делаю адаптивную схему интеграции. Каждый элемент объема, который пересекает границу интегрирования, дополнительно подразделяется. Мое определение типа «куб» приведено ниже. Мой ученик написал рабочий прототип на python, но я пытаюсь переписать его на julia для повышения производительности.

using Iterators
# Composite type defining a cube element of the integration domain
type cube
    pos # floats: Position of the cube in the integration domain
    dx  # float: Edge length of the cube
    verts # float: List of positions of the vertices 
    fvals::Dict # tuples,floats: Function values at the corners of the cube and its children
    depth::Int # int: Number of splittings to get to this level of cube
    maxdepth::Int # Deepest level of splitting (stopping condition)
    intVal # float: this cube's contribution to the integral

    intVal = 0

    cube(pos,dx,depth,maxdepth) = new(pos,dx,
           [i for i in product(0:dx:dx,0:dx:dx,0:dx:dx)],
           [vt=>fVal([vt...]) for vt in [i for i in product(0:dx:dx,0:dx:dx,0:dx:dx)]],
           depth,maxdepth,intVal)
end

person glwhart    schedule 29.05.2015    source источник


Ответы (2)


Внутренние конструкторы - это просто функции, которые появляются внутри блока типа с тем же именем, что и тип. Это означает, что вы можете использовать для их определения любой синтаксис функций. Вы можете использовать короткую форму (как вы это делали выше) или вместо этого использовать более подробный синтаксис блока:

type test1
   a
   b
   c
   function test1(a,b)
      c = a/b
      return new(a,b,c)
   end
end

Вызов new даже не обязательно должен быть последним выражением в методе; вы можете присвоить его результат промежуточной переменной, а затем вернуть его.


Еще несколько деталей: блок type похож на обычный Julia область за некоторыми исключениями:

  • Любые символы, которые появляются отдельно или с аннотацией типа, становятся полями типа.
  • Функции имеют доступ к специальной встроенной функции new для создания экземпляра типа, а функции с тем же именем, что и тип, становятся внутренним конструктором.

Когда вы помещаете любые другие назначения в блок типа, они просто создают локальную переменную в этой области. Это не поле или часть типа, а просто переменная, которую можно использовать в методах конструктора. Тем не менее, это не очень полезно и, скорее всего, изменится в будущем.

person mbauman    schedule 29.05.2015
comment
Это было действительно полезно. Спасибо. Мне особенно понравились дополнительные детали в конце и ваша модификация моего второго примера, которая делает именно то, что я хотел. Я не понимал, что другой синтаксис функции (блочный синтаксис) может использоваться для внутреннего конструктора. Мне следовало прочитать подробнее Case Study: Rational осторожно - person glwhart; 29.05.2015

Используя ответ Мэтта Б., я построил следующий ответ для своего конкретного случая использования. Использование блочного синтаксиса для функции намного чище.

using Iterators
# Composite type defining a cube element of the integration domain
type cube
    pos # floats: Position of the cube in the integration domain
    dx  # float: Edge length of the cube
    verts # tuple: List of positions of the vertices 
    fvals # floats: Function values at the corners of the cube and its children
    depth::Int # int: Number of splittings to get to this level of cube
    maxdepth::Int # Deepest level of splitting (stopping condition)

    function cube(pos,dx,depth,maxdepth)
        verts = [pos+[vt...].*dx for vt in product(0:1,0:1,0:1)]
        fvals = [fVal([vt...]) for vt in verts ]
        return new(pos,dx,verts,fvals,depth,maxdepth)
    end
end
person glwhart    schedule 29.05.2015