仅使用少量更改定义新方法

时间:2015-06-20 14:53:56

标签: julia

我想写一个接受补充参数的版本。与初始版本的区别仅在于几行代码,可能在循环内。一个典型的例子是使用权重w的向量。

一种解决方案是完全重写新功能

function f(Vector::a)
   ...
   for x in a
      ...
      s += x[i]
      ... 
   end
   ...
end

function f(a::Vector, w::Vector)
   ... 
   for x in a
     ...
     s += x[i] * w[i]
     ...
   end
   ...
end

此解决方案复制代码,因此使程序更难维护。 我可以将...拆分为不同的辅助函数,这两个函数都由两个函数调用,但生成的代码很难遵循

另一个解决方案是只编写一个函数,并为每一行应该改变使用? :结构

function f(a, w::Union(Nothing, Vector) = nothing)
   ....
   for x in a
      ...
      s += (w == nothing)? x[i] : x[i] * w[i]
      ...
   end
   ....
end

此代码需要检查循环中每个步骤的条件,与第一个版本相比,这听起来效率不高。

我确信有更好的解决方案,可能使用宏。什么是处理这个问题的好方法?

3 个答案:

答案 0 :(得分:4)

有很多方法可以做这种事情,从可选参数到自定义类型到使用@eval代码生成的元编程(这将为每个新方法的变化进行拼接循环一系列可能性。)

我认为在这种情况下,我会使用@ColinTBowers和@GnimucKey建议的方法的组合。

定义全部ones的自定义数组类型相当简单:

immutable Ones{N} <: AbstractArray{Int,N}
    dims::NTuple{N, Int}
end
Base.size(O::Ones) = O.dims
Base.getindex(O::Ones, I::Int...) = (checkbounds(O, I...); 1)

我选择使用Int作为元素类型,因为它往往会很好地促进。现在您所需要的只是在您的参数列表中更灵活一点,并且您将会感到高兴:

function f(a::Vector, w::AbstractVector=Ones(size(a))
    …

这应该比其他任何提议的解决方案具有更低的开销; getindex应该很好地内联作为边界检查和数字1,没有类型不稳定,并且您不需要重写算法。如果您确定所有访问都是入站的,您甚至可以删除边界检查作为额外的优化。或者在最近的0.4上,你可以定义并使用Base.unsafe_getindex(O::Ones, I::Int...) = 1(因为它不能保证为所有的AbstractArrays定义,所以它不会在0.3上工作。)

答案 1 :(得分:3)

在这种情况下,使用Optional Arguments可能会起到作用。

只需将w参数默认设为ones()

答案 2 :(得分:2)

我几次遇到这个问题。如果你想避免循环中的条件if语句,一种可能性就是在一些虚拟类型上使用多个分派。例如:

abstract MyFuncTypes

type FuncWithNoWeight <: MyFuncTypes; end
evaluate(x::Vector, i::Int, ::FuncWithNoWeight) = x[i]

type FuncWithWeight{T} <: MyFuncTypes
    w::Vector{T}
end
evaluate(x::Vector, i::Int, wT::FuncWithWeight) = x[i] * wT.w[i]


function f(a, w::MyFuncTypes=FuncWithNoWeight())
    ....
    for x in a
        ...
        s += evaluate(x, i, w)
        ...
    end
    ....
end

我将evaluate方法扩展到FuncWithNoWeightFuncWithWeight以获得适当的行为。我还将这些类型嵌套在抽象类型MyFuncTypes中,这是f的第二个输入(默认值为FuncWithNoWeight)。从这里开始,多个派遣和朱莉娅的类型系统负责其余部分。

这种方法的一个巧妙之处在于,如果您稍后决定要在循环内添加第三种类型的行为(不一定是加权,几乎任何类型的转换都是可能的),它就像定义新类型,将其嵌套在MyFuncTypes下,并将evaluate方法扩展为新类型。

更新:正如Matt B.指出的那样,我的答案的第一个版本意外地将类型不稳定性引入到我的解决方案的函数中。作为一般规则,我通常会发现,如果马特发布了一些值得密切关注的事情(提示,提示,请查看他的答案)。我还在学习很多关于Julia的知识(我正在回答有关StackOverflow的问题以促进学习)。我已经更新了我的答案,以消除Matt指出的类型不稳定性。

相关问题