Подтвердить что ты не робот

Какой внутренний конструктор?

TL; DR:

  • какое точное определение внутренних конструкторов? В Julia-v0.6 +, правильно ли сказать "любой конструктор, который можно вызывать с сигнатурой typename{...}(...) (обратите внимание на часть {}), является внутренним конструктором"?
  • Как обсуждалось в комментарии ниже, конструктор только для внешнего интерфейса фактически explicit inner constructor?
  • Можно ли использовать methods для проверки того, является ли метод внутренним/внешним конструктором?
  • Какая разница между конструкторами по умолчанию, которые автоматически определяются Julia и соответствующими, явно определенными пользователями?

Кстати, я знаю, как использовать и когда использовать внутренний конструктор. Я знал, что такое внутренний конструктор, до тех пор, пока внешние конструкторы не войдут и не замутнут воды.:(

Вспомним некоторые утверждения из doc:

1. Методы внешнего конструктора

Конструктор аналогичен любой другой функции в Julia, поскольку его общее поведение определяется комбинированным поведением его методов.

2. Внутренние конструкторские методы

Метод внутреннего конструктора очень похож на метод внешнего конструктора с двумя отличиями: 1. Он объявляется внутри блока объявления типа, а не вне его, как обычные методы. 2. Он имеет доступ к специальной локально существующей функции под названием new, которая создает объекты типа блока.

3. Параметрические конструкторы

Без каких-либо явно предоставляемых внутренних конструкторов объявление составного типа Point{T<:Real} автоматически предоставляет внутренний конструктор Point{T} для каждого возможного типа T<:Real, который ведет себя так же, как и непараметрические внутренние конструкторы по умолчанию. Он также предоставляет один общий внешний конструктор Point, который принимает пары реальных аргументов, которые должны быть одного типа.

Я обнаружил, что inner constructor methods не может быть непосредственно наблюдаемым methods, даже methods(Foo{Int}) работает, он фактически не "точно так же, как любая другая функция", общие общие функции не могут быть methods ed таким образом.

julia> struct Foo{T}
    x::T
end

julia> methods(Foo)
# 2 methods for generic function "(::Type)":
(::Type{Foo})(x::T) where T in Main at REPL[1]:2  # outer ctor  「1」
(::Type{T})(arg) where T in Base at sysimg.jl:24  # default convertion method「2」

julia> @which Foo{Int}(1) # or methods(Foo{Int})
(::Type{Foo{T}})(x) where T in Main at REPL[1]:2 # inner ctor 「3」

Тем не менее, внешние конструкторы добавляет еще одну морщину в историю конструктора:

julia> struct SummedArray{T<:Number,S<:Number}
           data::Vector{T}
           sum::S
           function SummedArray(a::Vector{T}) where T
               S = widen(T)
               new{T,S}(a, sum(S, a))
           end
       end
julia> methods(SummedArray)
# 2 methods for generic function "(::Type)":
(::Type{SummedArray})(a::Array{T,1}) where T in Main at REPL[1]:5 # outer ctor「4」
(::Type{T})(arg) where T in Base at sysimg.jl:24

Hmmm, a outer constructor В блоке объявления типа, и он также вызывает new. Я предполагаю, что цель здесь состоит в том, чтобы не допустить, чтобы Джулия определяла для меня внутреннюю конструкторскую пару по умолчанию, но является ли второе утверждение из документации по-прежнему истинным в этом случае? Это запутывает новых пользователей.

Здесь, я прочитал другую форму внутренних конструкторов:

julia> struct Foo{T}
     x::T
     (::Type{Foo{T}})(x::T) = new{T}(x) 
   end

julia> methods(Foo)
# 1 method for generic function "(::Type)":
(::Type{T})(arg) where T in Base at sysimg.jl:24

julia> methods(Foo{Int})
# 2 methods for generic function "(::Type)":
(::Type{Foo{T}})(x::T) where T in Main at REPL[2]:3  「5」
(::Type{T})(arg) where T in Base at sysimg.jl:24

Это далеко от канонической формы Foo{T}(x::T) where {T} = new(x), но, похоже, результаты совершенно одинаковы.

Итак, мой вопрос - точное определение внутренних конструкторов? В Julia-v0.6 +, правильно ли сказать "любой конструктор, который можно вызывать с сигнатурой typename{...}(...) (обратите внимание на часть {}), является внутренним конструктором"?

4b9b3361

Ответ 1

В качестве примера предположим, что вы хотите определить тип для представления четных чисел:

julia> struct Even
          e::Int
       end

julia> Even(2)
Even(2)

До сих пор так хорошо, но вы также хотите, чтобы конструктор отклонил нечетные числа, и пока Even(x) не делает:

julia> Even(3)
Even(3)

Итак, вы пытаетесь написать свой собственный конструктор как

julia> Even(x) = iseven(x) ? Even(x) : throw(ArgumentError("x=$x is odd"))
Even

и... барабан, пожалуйста... Это не работает:

julia> Even(3)
Even(3)

Почему? Позвольте спросить у Джулии, что она только что назвала:

julia> @which Even(3)
Even(e::Int64) in Main at REPL[1]:2

Это не тот метод, который вы определили (посмотрите имя аргумента и тип), это неявно предоставленный конструктор. Может быть, нам следует переопределить это? Ну, не пробуйте это дома:

julia> Even(e::Int) = iseven(e) ? Even(e) : throw(ArgumentError("e=$e is odd"))
Even
julia> Even(2)
ERROR: StackOverflowError:
Stacktrace:
 [1] Even(::Int64) at ./REPL[11]:0
 [2] Even(::Int64) at ./REPL[11]:1 (repeats 65497 times) 

Мы только что создали бесконечный цикл: мы переопределили Even(e), чтобы рекурсивно называть себя. Теперь мы сталкиваемся с проблемой курицы и яйца: мы хотим переопределить неявный конструктор, но нам нужен другой конструктор для вызова определенной функции. Как мы видели, вызов Even(e) не является жизнеспособным вариантом.

Решение состоит в том, чтобы определить внутренний конструктор:

julia> workspace()

julia> struct Even
          e::Int
          Even(e::Int) = iseven(e) ? new(e) : throw(ArgumentError("e=$e is odd"))
       end


julia> Even(2)
Even(2)

julia> Even(3)
ERROR: ArgumentError: e=3 is odd
..

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

julia> Even() = new(2)
Even

julia> Even()
ERROR: UndefVarError: new not defined 
..