在Julia中克隆一个函数 [英] Clone a function in Julia

查看:58
本文介绍了在Julia中克隆一个函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用它的旧定义覆盖Julia中的一个函数.这样做的方法似乎是克隆功能并使用副本覆盖原始功能-类似于以下内容.但是,似乎deepcopy(f)只是返回了对f的引用,因此这是行不通的.

I want to overwrite a function in Julia using its old definition. It seems the way to do this would be to clone the function and overwrite the original using the copy — something like the following. However, it appears deepcopy(f) just returns a reference to f, so this doesn't work.

f(x) = x
f_old = deepcopy(f)
f(x) = 1 + f_old(x)

如何克隆功能?

背景:写一个宏@override很有趣,它允许我逐点(甚至可能逐段)重写函数.

Background: I'm interesting in writing a macro @override that allows me to override functions pointwise (or maybe even piecewise).

fib(n::Int) = fib(n-1) + fib(n-2)
@override fib(0) = 1
@override fib(1) = 1

使用 @memoize 可以使此特定示例运行缓慢,并使效率更高.可能有充分的理由不这样做,但是在某些情况下,当定义一个功能时,可能还不完全了解该功能,因此有必要进行覆盖.

This particular example would be slow and could be made more efficient using @memoize. There may be good reasons not to do this, but there may also be situations in which one does not know a function fully when it is defined and overriding is necessary.

推荐答案

我们可以使用 IRTools来做到这一点. jl .

(注意,在较新版本的IRTools上,您可能需要输入IRTools.Inner.code_ir而不是IRTools.code_ir.)

(Note, on newer versions of IRTools, you may need to ask for IRTools.Inner.code_ir instead of IRTools.code_ir.)

using IRTools

fib(n::Int) = fib(n-1) + fib(n-2)

const fib_ir  = IRTools.code_ir(fib, Tuple{Int})
const fib_old = IRTools.func(fib_ir)

fib(n::Int) = n < 2 ? 1 : fib_old(fib, n)

julia> fib(10)
89

我们在这里所做的工作捕获了功能fib的中间表示形式,然后将其重建为一个称为fib_old的新功能.然后我们可以自由地用fib_old覆盖fib的定义!请注意,由于fib_old被定义为递归调用fib,而不是fib_old,因此,当我们调用fib(10)时,没有堆栈溢出.

What we did there was captured the intermediate representation of the function fib, and then rebuilt it into a new function which we called fib_old. Then we were free to overwrite the definition of fib in terms of fib_old! Notice that since fib_old was defined as recursively calling fib, not fib_old, there's no stack overflow when we call fib(10).

要注意的另一件事是,当我们调用fib_old时,我们写的是fib_old(fib, n)而不是fib_old(n).这是由于IRTools.func的工作方式.

The other thing to notice is that when we called fib_old, we wrote fib_old(fib, n) instead of fib_old(n). This is due to how IRTools.func works.

根据Slack上的Mike Innes的说法:

According to Mike Innes on Slack:

在Julia IR中,所有函数都采用一个隐藏的额外参数来表示函数本身 原因是闭包是带有字段的结构,您需要在IR中访问

In Julia IR, all functions take a hidden extra argument that represents the function itself The reason for this is that closures are structs with fields, which you need access to in the IR

这是您的@override宏的实现,语法略有不同:

Here's an implementation of your @override macro with a slightly different syntax:

function _get_type_sig(fdef)
    d = splitdef(fdef)
    types = []
    for arg in d[:args]
        if arg isa Symbol
            push!(types, :Any)
        elseif @capture(arg, x_::T_) 
            push!(types, T)
        else
            error("whoops!")
        end
    end
    if isempty(d[:whereparams])
        :(Tuple{$(types...)})
    else
        :((Tuple{$(types...)} where {$(d[:whereparams]...)}).body)
    end
end

macro override(cond, fdef)
    d = splitdef(fdef)
    shadowf = gensym()
    sig = _get_type_sig(fdef)
    f = d[:name]
    quote
        const $shadowf = IRTools.func(IRTools.code_ir($(d[:name]), $sig))
        function $f($(d[:args]...)) where {$(d[:whereparams]...)}
            if $cond
                $(d[:body])
            else
                $shadowf($f, $(d[:args]...))
            end
        end
    end |> esc
end

现在可以键入

fib(n::Int) = fib(n-1) + fib(n-2)
@override n < 2 fib(n::Int) = 1

julia> fib(10)
89

最好的部分是,这几乎和我们将条件写入原始函数中一样快(在运行时,而不是编译时!).

The best part is that this is nearly as fast (at runtime, not compile time!) as if we had written the conditions into the original function!

n = 15

fib2(n::Int) = n < 2 ? 1 : fib2(n-1) + fib2(n-2)

julia> @btime fib($(Ref(15))[])
  4.239 μs (0 allocations: 0 bytes)
89

julia> @btime fib2($(Ref(15))[])
  3.022 μs (0 allocations: 0 bytes)
89

这篇关于在Julia中克隆一个函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆