在Julia语言中,是否有可能在运行时创建类型?

10

假设我想要一个形如下面这样的函数

abstract RecordType
function CreateRecordType(fields_names::Vector{ASCIIString}, type_name::ASCIIString)
    # magic that creates the type type_name with string fields field_names
end
例如,CreateRecordType(["name","age"], "Person")会创建一个新类型,具有以下定义:
type Person <: RecordType
    name::ASCIIString
    age::ASCIIString
end

这个在Julia中是否可以实现?

1个回答

8

方法一:解析以获取AST,然后进行评估

可能最简单的方法是创建一个所需内容的字符串,然后解析该字符串以获取AST,再对AST进行评估。您可以在函数内部执行任何或所有操作。以下是可能的简单实现,它可以完成所有这些操作。

function CreateRecordType(typeName::ASCIIString,fieldNames::Array{ASCIIString,1},fieldTypes::Array{ASCIIString,1})
   s::ASCIIString = "type $typeName <: RecordType\n";

   for i = 1:length(fieldNames)
      s = s*"$(fieldNames[i])::$(fieldTypes[i])\n"
   end
   eval(parse(s*"end"));
   return;
end

使用它...

julia> abstract RecordType;

julia> CreateRecordType("Person",["name","age"],["ASCIIString","Int64"])

julia> bubba = Person("Bubba",2)
Person("Bubba",2)

julia> print(bubba.age)
2

这可能不是最有效的方法,相反,您可以查看解析产生的AST,然后创建 Expr 以直接生成AST,而不使用解析和字符串。
第二种方法:直接创建AST
这是一种创建AST的替代形式,它更加安全,因为它需要类型和符号,而不是不透明的字符串。 上面提到过,这是通过试验各种解析输出创建的。 使用 Dict 而不是2个数组可能更好,因为字段和类型必须始终成对出现。
function createTypeAST(typeName::Symbol,parentType::Type,fields::Dict{Symbol,DataType})
   e = Expr(:type,true)
   push!(e.args,Expr(:<:));
   push!(e.args[2].args,typeName);
   push!(e.args[2].args,parentType);
   push!(e.args,Expr(:block))
   for field in fields
      push!(e.args[3].args,:($(field[1])::$(field[2])));
   end
   return e;
end

第二个表单的实现

julia> x = createTypeAST(:P1,RecordType,Dict(:a => Int64))
:(type P1<:RecordType
        a::Int64
    end)

julia> eval(x)

julia> y = P1(1)
P1(1)

julia> y.a
1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接