如何在 julia 中找到现有函数 "aliases"?
How can I find existing function "aliases" in julia?
将事情放在上下文中:
a) 在 julia 中,可以通过 repeat("foo",n)
.
重复字符串 "foo"
n 次
我试图找出是否为此定义了一个 "symbolic operator" 别名("alias" 在此处用于更广泛的意义),因为我怀疑可能会有,我发现了通过检查定义了 repeat(::String,::Integer)
(types.jl
) 的文件,它是 "foo" ^ n
; ^
在下面明确定义,它基本上是 repeat
.
的包装器
- 有了这些知识,我在 REPL 中输入
less(^, (String,Int64))
进行确认,这确实将我带到了同一个文件和行。
- 如果我在 REPL 中输入
^
,我会得到 ^ (generic function with 47 methods)
b) ×
是 cross
的别名,即调用 [1.,2.,3.] × [1.,2.,3.]
和 cross([1.,2.,3.],[1.,2.,3.])
似乎是等价的,而 is(×,cross)
returns true
.
- 如果我在终端中输入
×
,我会得到 cross (generic function with 1 method)
;
- 如果我键入
less(×,(AbstractVector,AbstractVector))
,它会将我直接带到 cross
的定义,而不是 ×
的明确定义;
- 经过一番 "grepping" 我在
sysimg.jl
. 中找到了定义 const × = cross
Q1: Is there an easier programmatic way of checking if a function has an alias defined? i.e. is there an equivalent concept to:
julia> findaliases(cross)
result: [×]
(I tried matching :×
to :cross
using a conditional list comprehension on names(Base)
, but without much luck)
Q2: Is there a programmatic way of doing the same for repeat(::String,::Int64)
resulting in (^)(::AbstractString, ::Integer)
, given that technically it's not an alias, but a wrapper?, i.e.
julia> findwrappers(repeat,(String,Int64))
result: [(^,(AbstractString, Integer))]
Or, in general, is there a convention on where / how such wrappers are defined that might help me to figure it out?
第一个问题,请尝试
julia> [x for x in names(Base) if eval(Base, x) === cross]
2-element Array{Symbol,1}:
:cross
:×
第二个,我不知道是否有比 grep
ping 更好的方法。我想,有 "brute force":
julia> [x for x in names(Base.Operators) if try eval(:(Base.Operators.$x("x", 3))) end == "xxx"]
1-element Array{Symbol,1}:
:^
...我真的不推荐。但如果有效...
关于第二个:
寻找包裹其他东西的东西。
以下是最终解决方案的更多解决方案的开始,
因为很难确定程序员的意图。
特别是因为一个函数包装另一个函数意味着什么的概念并没有明确定义。
还是从这个解决方案,我认为你可以合理地调整它以适应你自己对一个函数包装另一个函数意味着什么的定义。
我相信 wraps 函数的作用与您的 findwrappers
相反,
但总的来说,你可以粗略地反转它,通过将它应用于所有加载模块中的 everything (实际上不会花费太长时间,之前做过类似的 - Base 最多可能需要 30 分钟)
目前的方法是召回率高于精度。
也就是说它宁愿return一个误报,
比错过一个。
方法
"""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
```
使用示例
因为 wraps 本身有一个 wrapper 定义
julia>wraps(wraps)
Dict{String,String} with 1 entry:
"wraps(ff, arg_types) at In[217]:33" => "Main.wraps"
接下来的几个示例使用 "ugly" 但信息丰富的输出。
显示我们对正在包装的内容的猜测,以及完整的行,以便您可以检查该猜测:
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
做的一样
结论
这是一个启发式的解决方案。
它的回想是我认为非常好的。
我怀疑知道包装器是否是包装器的精确度也很好,但是能够弄清楚它是包装器的用途的能力不是。
一般来说,有 1 行长的 AST(在 nothing
的明显强制性第一行之后)并且看起来像
Expr
head: Symbol return
args: Array{Any}((1,))
1: Expr
head: Symbol call
强烈建议该方法是包装器。
因为这个方法唯一做的就是调用其他函数。
确定该功能是什么是困难的部分。
您可能会注意到我只找到了函数的名称——没有找到它们的 arg 类型——所以没有找到方法。可以通过在 AST 中查找 _1
、_2
等来计算出 arg 类型——它们映射到进行包装的方法的参数。但这是 reader.
的练习
将事情放在上下文中:
a) 在 julia 中,可以通过 repeat("foo",n)
.
"foo"
n 次
我试图找出是否为此定义了一个 "symbolic operator" 别名("alias" 在此处用于更广泛的意义),因为我怀疑可能会有,我发现了通过检查定义了 repeat(::String,::Integer)
(types.jl
) 的文件,它是 "foo" ^ n
; ^
在下面明确定义,它基本上是 repeat
.
- 有了这些知识,我在 REPL 中输入
less(^, (String,Int64))
进行确认,这确实将我带到了同一个文件和行。 - 如果我在 REPL 中输入
^
,我会得到^ (generic function with 47 methods)
b) ×
是 cross
的别名,即调用 [1.,2.,3.] × [1.,2.,3.]
和 cross([1.,2.,3.],[1.,2.,3.])
似乎是等价的,而 is(×,cross)
returns true
.
- 如果我在终端中输入
×
,我会得到cross (generic function with 1 method)
; - 如果我键入
less(×,(AbstractVector,AbstractVector))
,它会将我直接带到cross
的定义,而不是×
的明确定义; - 经过一番 "grepping" 我在
sysimg.jl
. 中找到了定义
const × = cross
Q1: Is there an easier programmatic way of checking if a function has an alias defined? i.e. is there an equivalent concept to:
julia> findaliases(cross) result: [×]
(I tried matching
:×
to:cross
using a conditional list comprehension onnames(Base)
, but without much luck)
Q2: Is there a programmatic way of doing the same for
repeat(::String,::Int64)
resulting in(^)(::AbstractString, ::Integer)
, given that technically it's not an alias, but a wrapper?, i.e.julia> findwrappers(repeat,(String,Int64)) result: [(^,(AbstractString, Integer))]
Or, in general, is there a convention on where / how such wrappers are defined that might help me to figure it out?
第一个问题,请尝试
julia> [x for x in names(Base) if eval(Base, x) === cross]
2-element Array{Symbol,1}:
:cross
:×
第二个,我不知道是否有比 grep
ping 更好的方法。我想,有 "brute force":
julia> [x for x in names(Base.Operators) if try eval(:(Base.Operators.$x("x", 3))) end == "xxx"]
1-element Array{Symbol,1}:
:^
...我真的不推荐。但如果有效...
关于第二个: 寻找包裹其他东西的东西。 以下是最终解决方案的更多解决方案的开始, 因为很难确定程序员的意图。 特别是因为一个函数包装另一个函数意味着什么的概念并没有明确定义。 还是从这个解决方案,我认为你可以合理地调整它以适应你自己对一个函数包装另一个函数意味着什么的定义。
我相信 wraps 函数的作用与您的 findwrappers
相反,
但总的来说,你可以粗略地反转它,通过将它应用于所有加载模块中的 everything (实际上不会花费太长时间,之前做过类似的 - Base 最多可能需要 30 分钟)
目前的方法是召回率高于精度。 也就是说它宁愿return一个误报, 比错过一个。
方法
"""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
```
使用示例
因为 wraps 本身有一个 wrapper 定义
julia>wraps(wraps)
Dict{String,String} with 1 entry:
"wraps(ff, arg_types) at In[217]:33" => "Main.wraps"
接下来的几个示例使用 "ugly" 但信息丰富的输出。 显示我们对正在包装的内容的猜测,以及完整的行,以便您可以检查该猜测:
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
结论
这是一个启发式的解决方案。 它的回想是我认为非常好的。 我怀疑知道包装器是否是包装器的精确度也很好,但是能够弄清楚它是包装器的用途的能力不是。
一般来说,有 1 行长的 AST(在 nothing
的明显强制性第一行之后)并且看起来像
Expr
head: Symbol return
args: Array{Any}((1,))
1: Expr
head: Symbol call
强烈建议该方法是包装器。 因为这个方法唯一做的就是调用其他函数。 确定该功能是什么是困难的部分。
您可能会注意到我只找到了函数的名称——没有找到它们的 arg 类型——所以没有找到方法。可以通过在 AST 中查找 _1
、_2
等来计算出 arg 类型——它们映射到进行包装的方法的参数。但这是 reader.