如何在朱莉娅中找到现有的功能“别名”?

时间:2016-09-03 00:18:32

标签: alias julia

把事情放到上下文中:

a)在朱莉娅,人们可以通过"foo"重复字符串repeat("foo",n) n

我试图找出是否为此定义了一个“符号运算符”别名(这里更广义地使用了“别名”),因为我怀疑可能存在,我发现它是{{1}通过检查定义"foo" ^ n的文件(repeat(::String,::Integer)); types.jl在下面明确定义,它基本上是^的包装。

  • 凭借这些知识,我在REPL中键入repeat进行确认,这确实把我带到了同一个文件和行。
  • 如果我在REPL中输入less(^, (String,Int64)),我会^

b)^ (generic function with 47 methods)×的别名,即调用cross[1.,2.,3.] × [1.,2.,3.]似乎相同,cross([1.,2.,3.],[1.,2.,3.])返回is(×,cross) }。

  • 如果我在终端中输入true,我会×;
  • 如果我输入cross (generic function with 1 method),我会直接找到 less(×,(AbstractVector,AbstractVector)) 的定义,而不是cross的明确定义;
  • 经过一些“贪图”之后,我在×中找到了const × = cross这个定义。
  

Q1:是否有更简单的编程方式来检查函数是否定义了别名?即是否存在与...相同的概念:

sysimg.jl
     

(我尝试使用julia> findaliases(cross) result: [×] 上的条件列表理解将:cross匹配,但没有太多运气)

  

Q2:是否有一种编程方式为names(Base)做同样的事情导致repeat(::String,::Int64),因为从技术上讲它不是别名,而是包装器?,即< / p>

(^)(::AbstractString, ::Integer)
     

或者,一般来说,是否存在关于在何处/如何定义这些包装器的约定,这可能有助于我弄清楚它?

2 个答案:

答案 0 :(得分:3)

关于第一个问题,请尝试

julia> [x for x in names(Base) if eval(Base, x) === cross]
2-element Array{Symbol,1}:
 :cross
 :×

对于你的第二个,我不知道是否有比grep ping更好的方法。我想,有“蛮力”:

julia> [x for x in names(Base.Operators) if try eval(:(Base.Operators.$x("x", 3))) end == "xxx"]
1-element Array{Symbol,1}:
 :^

...我不是真的推荐。但如果它有效......

答案 1 :(得分:3)

关于第二个问题: 找出包装其他东西的东西。 以下是最终解决方案的解决方案的开始, 因为确定程序员的意图很难。 特别是因为关于功能包装另一个功能的含义的概念没有明确定义。 仍然从这个解决方案中我认为你可以合理地调整它以适合你自己对函数包装另一个函数的定义。

我相信包裹功能与你的findwrappers相反, 但一般来说,你可以通过将它应用到所有已加载模块中的所有来粗略地反转它(实际上不需要太长时间,之前已经完成了类似的事情 - 可能是基地的30分钟顶部)

此刻的方法适用于Recall over Precision。 也就是说它宁可回归假阳性, 比错过一个。

方法

"""AST at this level often uses GlobalRefs -- define a way to represent them"""
Symbol(fgr::GlobalRef) = Symbol(module_name(fgr.mod),".", fgr.name)

"""
Returns a Nullable{tuple}. It is null if we do not wrap anything, or if we do then:
The first is a Symbol representing out best guess at what we are wrapping's name
The second is the whole last line, so you can work out for yourself what we are wrapping.
"""
function wraps(m::Method)
    ast = Base.uncompressed_ast(m)

    if ast[1]==nothing && length(ast)==2
        rline = ast[2]
        if rline.head == :return
            if rline.args[1].head == :call

                cline = rline.args[1]
                function_called = cline.args[1]
                if (typeof(function_called)<:GlobalRef 
                    && function_called.mod==Core 
                    && function_called.name==Symbol("_apply")) #Then calling a Callable-nonfunction

                    return  Nullable((Symbol(cline.args[2]), cline))
                else #It is a function call
                    return  Nullable((Symbol(function_called), cline))
                end
            end
        end

    end
    Nullable{Tuple{Symbol, Expr}}()
end

wraps(ff, arg_types) = wraps(first(methods(ff, arg_types)))

"Convience method, to produce nice simple list"
function wraps(ff)
    rets = Dict{String, String}()
    for m in methods(ff)
        wrapped = wraps(m)
        if !isnull(wrapped)
            me = string(m)
            them = string(first(get(wrapped)))
            rets[me] = them
        end
    end
    return rets
end

```

使用示例

因为换包本身有一个包装器定义

julia>wraps(wraps)
Dict{String,String} with 1 entry:
  "wraps(ff, arg_types) at In[217]:33" => "Main.wraps"

接下来的几个例子使用“丑陋”但信息丰富的输出。 显示我们对包裹内容的猜测,以及整行,以便您可以检查猜测:

julia>wraps(^, (String, Int64))
Nullable{Tuple{Symbol,Expr}}((Symbol("Base.repeat"),:((Base.repeat)(_2,_3))))

另一个,这次它包装的东西不是真正的功能。 Base.string

julia> wraps(*,(String, String))
Nullable{Tuple{Symbol,Expr}}((Symbol("Base.string"),:((Core._apply)(Base.string,(Core.tuple)(_2),_3))))

显示它没有找到不存在的包装器(因为^包装,repreat而不是相反)

julia> wraps(repeat, (String, Int64))
Nullable{Tuple{Symbol,Expr}}()

parse的一个包装器,实际上是非常通用的。 _1表示它的第一个参数 因为parse(T,c::Char)会回归到T(c) 所以parse实际上(动态地)包装了它的第一个参数。 您可能不喜欢wrap的定义,因此您可能必须通过修改wraps函数来排除它。

julia>wraps(parse)

Dict{String,String} with 6 entries:
  "parse{T<:Integer}(::Type{T}, s::AbstractString, base::Integer) at parse.jl:150" => "Base.get"
  "parse{T<:Integer}(::Type{T}, c::Char) at parse.jl:6" => "_1"
  "parse{T<:Integer}(::Type{T}, s::AbstractString) at parse.jl:152" => "Base.get"
  "parse(stream::IO) at markdown/Julia/interp.jl:4" => "Markdown.#parse#84"
  "parse(str::AbstractString, pos::Int64) at parse.jl:179" => "Base.#parse#232"
  "parse(str::AbstractString) at parse.jl:194" => "Base.#parse#233"

所以现在是一个真正复杂的, 该方法错误的地方,有时

julia> wraps(^)

Dict{String,String} with 26 entries:
  "^(x, p::Integer) at intfuncs.jl:144" => "Base.power_by_squaring"
  "^(x::BigInt, y::BigInt) at gmp.jl:434" => "GMP.bigint_pow"
  "^{T<:Integer}(z::Complex{T}, n::Integer) at complex.jl:572" => "Base.power_by_squaring"
  "^(::Irrational{:e}, x::Irrational) at irrationals.jl:181" => "Base.exp"
  "^(x::Irrational, y::Irrational) at irrationals.jl:95" => "Base.^"
  "^(s::AbstractString, r::Integer) at strings/types.jl:178" => "Base.repeat"
  "^(x::Bool, y::Bool) at bool.jl:51" => "Base.|"
  "^{T<:AbstractFloat}(x::T, y::Rational) at rational.jl:358" => "Base.^"
  "^(x::Number, y::Rational) at rational.jl:357" => "Base.^"
  "^(x::BigInt, y::Integer) at gmp.jl:436" => "GMP.bigint_pow"
  "^(x::Float32, y::Integer) at math.jl:318" => "Math.box"
  "^(x::Integer, y::BigInt) at gmp.jl:437" => "GMP.bigint_pow"
  "^(x::Number, p::Integer) at intfuncs.jl:143" => "Base.power_by_squaring"
  "^(::Irrational{:e}, x::Number) at irrationals.jl:181" => "Base.exp"
  "^(x::Float64, y::Integer) at math.jl:316" => "Math.box"
  "^{T<:Integer}(x::T, p::T) at intfuncs.jl:142" => "Base.power_by_squaring"
  "^(x::Float16, y::Integer) at float16.jl:154" => "Base.Float16"
  "^{T<:Number}(x::T, y::T) at promotion.jl:235" => "Base.no_op_err"
  "^(z::Complex, n::Integer) at complex.jl:565" => "Base.^"
  "^(::Irrational{:e}, x::Rational) at irrationals.jl:181" => "Base.exp"
  "^(x::Integer, y::Bool) at bool.jl:52" => "Base.ifelse"
  "^(a::Float16, b::Float16) at float16.jl:136" => "Base.Float16"
  "^(x::Number, y::Number) at promotion.jl:194" => "Base.^"
  "^{T<:AbstractFloat}(x::Complex{T}, y::Rational) at rational.jl:359" => "Base.^"
  "^(x::Bool, y::BigInt) at gmp.jl:438" => "(Core.getfield)(Base.GMP.Base,:power_by_squaring)"
  "^(::Irrational{:e}, x::Integer) at irrationals.jl:181" => "Base.exp"

因此,要利用丑陋但信息丰富的重载来深入了解声称是Math.box包装器的问题:

julia> wraps(^, (Float32, Integer))

Nullable{Tuple{Symbol,Expr}}((Symbol("Math.box"),:((Base.Math.box)(Base.Math.Float32,(Base.Math.powi_llvm)((Base.Math.unbox)(Base.Math.Float32,_2),(Base.Math.unbox)(Base.Math.Int32,(Base.Math.Int32)(_3)))))))

所以是的,在这种情况下看起来像是Base.Math.powi_llvm的别名。 您可以在Wraps方法中定义一个特殊情况,以便通过Math.box调用来查找真实姓名。 就像我为Core._apply

做的那样

结论

这是一种启发式解决方案。 回想起来,我觉得非常好。 我怀疑它是否精确地知道某个包装器是否也非常好,但是能力可以解决它是什么包装器,不是。

一般来说,AST的长度为1行(显然是nothing的明确第一行之后),看起来像

Expr
  head: Symbol return
  args: Array{Any}((1,))
    1: Expr
      head: Symbol call

强烈建议该方法是一个包装器。 因为这个方法唯一能做的就是调用其他函数。 确定该功能是什么,是困难的部分。

您可能会注意到我只找到了函数的名称 - 而不是它们的arg类型 - 所以不是方法。通过在AST中查找_1_2等可以计算出arg类型,这些类型映射到正在进行换行的方法的参数。但这是留给读者的练习。