Julia 如何在模块中声明外部函数

Julia 如何在模块中声明外部函数,julia,Julia,我在Julia模块中编写了以下算法 module two_algo export two function two(type) a = construct(type) b = construct(type) result = construct(type) unity(a) unity(b) add(a,b,result) return result end end 有一段时间,我在一个模块外定义了这个算法,它可以编译并分派给任何定义了构造、统一和添加方法的类

我在Julia模块中编写了以下算法

module two_algo

export two

function two(type)
  a = construct(type)
  b = construct(type)
  result = construct(type)
  unity(a)
  unity(b)
  add(a,b,result)
  return result
end

end

有一段时间,我在一个模块外定义了这个算法,它可以编译并分派给任何定义了构造、统一和添加方法的类型。但是在一个模块中它不会编译。它不知道如何找到构造、统一和添加。我不想从某个特定模块导入它们,因为我可能有许多模块,每个模块都定义了construct、unity和add。我需要做什么来编译这个?在我以前的C/C++时代,我记得在模块中做了一些简单的事情,比如声明“extern construct;”。我真的希望我可以让two()函数采用单一类型,而不是传入实现construct、unity和add的函数。理想情况下,我也希望避免泛型。感谢您的指点。

首先,我想说的是,您可能不需要在这里使用模块:将项目组织在单独的文件中(便于您在源代码中轻松导航)可能是一个好主意,但是使用单独的模块会带来一些这里可能不需要的复杂性(例如,不存在需要使用单独的模块/名称空间来避免的名称冲突风险)

这就是说,这里组织事情的一种方法是使辅助函数(如
unity
add
)与使用它们的通用算法属于同一个模块。下面是一个简单的示例:

模块算法
导出算法
#这些函数只是声明的;它们没有方法
功能统一端
函数添加结束
函数algo(T)
a=单位(T)
加(a,a)
结束
结束
然后,定义新类型的代码的其他部分必须扩展模块中的函数以添加特定方法:

使用.Algo
结构MyType
val::Int
结束
#从模块'Algo'显式扩展'unity'和'add'函数`
#(而不仅仅是定义同名的新函数
算法统一(::Type{MyType})=MyType(1)
算法添加(a::MyType,b::MyType)=MyType(a.val+b.val)
这应该如预期的那样起作用:

julia> Algo.algo(MyType)
MyType(2)



编辑:一个更复杂的组织允许通过在所有其他模块都知道的“基本”模块中声明通用函数,将所有算法彼此隔离(也可能是所有类型):

module AlgoBase
    export unity, add
    
    function unity end
    function add end
end

module Algo1
    using ..AlgoBase  # allows using unity and add    
    export algo1

    function algo1(T)
        a = unity(T)
        add(a, a)
    end
end

module Types1
    using ..AlgoBase
    export Type1
    
    struct Type1
        val :: Int
    end

    # Explicitly extend the `unity` and `add` function from module `AlgoBase`
    # (instead of merely defining new functions with the same name
    AlgoBase.unity(::Type{Type1}) = Type1(1)
    AlgoBase.add(a::Type1, b::Type1) = Type1(a.val + b.val)
end

using .Algo1
using .Types1

algo1(Type1)

这或多或少是Julia生态系统的整个部分的设计方式,几个包协作提供与给定领域相关的算法,而不必相互依赖:他们只需要知道一个“基本”包(举例来说,它声明了许多有用的构建块,类型可以实现,算法可以使用)。然而,这样的体系结构通常用于构建完整的(和不相关的)软件包之间相互配合,也就是说,在单个软件包中没有真正的子模块规模。YMMV

这是对Francois解决我问题的答案的轻微修改。我仍然接受他的答案,因为他在发现和工作中贡献了最大的一部分

module UnityBase
    export unity
    
    function unity end
end

module AddBase
    export add
    
    function add end
end

module TwoAlgo
    using ..UnityBase, ..AddBase
    export two

    function two(T)
        a = unity(T)
        add(a, a)
    end
end

module MyTypeMod
    using ..AddBase, ..UnityBase
    export MyType
    
    struct MyType
        val :: Int
    end

    UnityBase.unity(::Type{MyType}) = MyType(1)
    AddBase.add(a::MyType, b::MyType) = MyType(a.val + b.val)
end

using .TwoAlgo
using .MyTypeMod

println(two(MyType))

要在我尝试模块化之前查看代码的一个小示例,请参见:谢谢Francois。我尚未完全测试您的解决方案。我无法编译示例代码。但我有一个问题。假设我有5个ALGO(Algo1、Algo2、Algo3、Algo4、Algo5),每个都使用unity()。然后我是否需要定义AlgoX.UNITYPE(::Type{MyType})=MyType(1)这五次?如果是这样的话,这是一个问题。你可以让一切工作,而不必定义多个
unity
函数。我可以更新我的答案来说明如何操作,但首先我必须问:为什么你首先要/需要为每个算法设置单独的模块?我只是遵循其他语言的惯例。在lon中g run我想写一个应用程序/库,它有一堆类型和一堆可重用的算法。保护所有东西不受其他任何东西的影响是有意义的。想象一下,至少有1000个算法和250个类型,每个类型至少有25个相关联的函数/方法。不确定遵循其他语言的约定是一个好主意;毕竟,Julia是一种不同的语言。我编辑了我的答案,以展示如何真正隔离库的每一个部分,但我认为,在Julia的世界中,这些技术大多被认为是过激的,除非我们谈论的是组织一个包的生态系统(而不是单个包中的子模块集合)新的解决方案也是有问题的。AlgoBase必须为所有类型的每个方法都有(无可否认是假的)签名(当然,某些类型会有一些重叠)。假设有些类型是数字类型,有些是颜色,有些是字符串,有些是特殊类型的文件,等等。与这些类型相关联的函数/方法种类繁多(有些类型的顺序类似于数字和字符串,有些不类似于复数).AlgoBase将更恰当地称为everythigbase,因为它将包含大量看似无关的签名。