julia参数构造函数 - 外部构造函数的问题

时间:2015-07-10 07:21:37

标签: types constructor julia

我正试图在2015年斯坦福大学的讲座中按照安德烈亚斯·诺亚克的草图实施GF类型,但是我很早就遇到了一些问题。我正在使用Julia 0.3.10

他的相关代码如下:

# Scalar finite fields
immutable GF{P,T<:Integer} <: Number
    data::T
    function GF(x::Integer)
        return new(mod(x, P))
    end
end

    # methods for scalar finite field
import Base: convert, inv, one, promote_rule, show, zero

function call{P}(::Type{GF{P}}, x::Integer)
    if !isprime(P)
        throw(ArgumentError("P must be a prime"))
    end
    return GF{P,typeof(x)}(mod(x, P))
end
convert{P,T}(::Type{GF{P,T}}, x::Integer) = GF{P}(x)
convert{P}(::Type{GF{P}}, x::Integer) = GF{P}(x)
convert{P,T}(::Type{GF{P,T}}, x::GF{P}) = GF{P,T}(x.data)
promote_rule{P,T1,T2<:Integer}(::Type{GF{P,T1}}, ::Type{T2}) = GF{P,promote_type(T1,T2
)}
show(io::IO, x::GF) = show(io, x.data)

因此,当您尝试定义类似这样的内容时会出现问题

GF{2}(11)

你得到了

  

类型无法构造

好的,所以没有自动构造函数。

GF{2,Int64}(11)效果很好。

问题在于没有自动构造函数,其他函数(如零(x))失败。

尝试创建外部构造函数并不适合我:

我认为GF{P}(x::Integer) = GF{P,Int64}(x)应该有效,但我得到了

  

警告:In [4]:1中GF的签名中不会出现静态参数P.   该方法不可调用。

基本上我已经没有关于如何指定调用的想法了 GF {3}(x)应创建GF {3,typeof(x)}(x)

的实例

我知道我错过了一些非常明显的事情。

由于

1 个答案:

答案 0 :(得分:5)

不幸的是,这在0.3上是不可能的。超载call的能力是0.4中可用的重要新功能之一。调用未完全参数化的类型(如GF{2})需要此功能。看起来Andreas在这个演示中使用了0.4-dev版本;如果您想直接关注他的演示,我建议您也这样做。

更多细节和解决方法:

在0.3中,通过简单地调用类型来调用类型的内部构造函数 - 但它必须是具体(或[leaf])类型。这意味着如果它具有类型参数,则必须完全参数化。在这种情况下,这意味着您需要手动指定整数类型才能调用内部构造函数:GF{2,Int}(5)

您还可以定义外部构造函数,它们的外观和行为就像一个碰巧具有相同基本名称的泛型函数。您也可以将类型参数添加到泛型函数中,但是虽然它们看起来类似于类型的参数(特别是当名称相同时),但它们的行为却截然不同!函数的参数定义了一个局部变量,用于匹配参数的类型。这就是为什么你的定义GF{P}(x::Integer) = GF{P,Int64}(x)会抛出警告的原因:因为你从不使用P来定义参数类型,所以Julia无法弄明白P应该是,所以它永远不会被调用。我们可以创建一个始终生成GF{2}

的函数
julia> GF2{T}(x::T) = GF{2,T}(x) # Call the fully parameterized inner constructor
GF2 (generic function with 1 method)

julia> GF2(3)
GF{2,Int64}(1)

请注意,当我致电T时,我没有说明GF2应该是什么 - 朱莉娅想出来了。当您为参数化类型定义外部构造函数时,这会让您感到困惑,因为GF{P}(x::Integer) = …是函数参数和类型参数看起来相同的重叠点!函数参数获胜,因此虽然您可以使用参数定义外部构造函数,但这些参数意味着不同的东西,并且在没有它们的情况下调用外部构造函数:GF(…)。您可以调用内部构造函数,但必须指定所有参数:GF{2, Int}(…)。 0.3中没有中间地带。

这改变为0.4:现在你可以定义调用任意对象时会发生什么!那是function call{P}(::Type{GF{P}}, x::Integer)定义的内容:如果您调用不完整类型GF{2},则会使用P=2调用此方法。实际上,这概括了外部构造函数。外部构造函数(即使它被参数化)只是一个&#34;糖&#34;用于为没有任何参数的call(::Type{GF}, x::Integer)类型定义GF。所以这就是0.4如何通过调用重载启用各种优秀行为。

如果你真的想让这个工作在0.3上,你可以定义上面GF2的函数来硬编码P值,或者你可以使类型不那么灵活:

immutable GF{P} <: Number
    data::Int
    function GF(x::Integer)
        return new(mod(convert(Int, x), P))
    end
end

现在内部构造函数正是您想要的:GF{P},因此您可以直接调用GF{2}(5)。但是您在使用的整数类型中失去了灵活性。还有其他一些技巧,但那是另一个时间。

相关问题