【问题标题】:Julia generics function type parameterJulia 泛型函数类型参数
【发布时间】:2020-10-31 16:12:57
【问题描述】:

我定义了一个函数如下:

function approx_pi(n)
    tot = Float64(0.0)
    for i in 1:n
        x = rand()
        y = rand()
        if x^2 + y^2 < 1
            tot+=1
        end
    end
    tot / n * 4
end

println(approx_pi(100_000_000))

我想使用相同的函数,但返回一个 Float128:

using Quadmath

function approx_pi(n)
    tot = Float128(0.0)
    for i in 1:n
        x = rand()
        y = rand()
        if x^2 + y^2 < 1
            tot+=1
        end
    end
    tot / n * 4
end

println(approx_pi(100_000_000))

我认为有一种方法可以通过等效的 C# 或 Java 泛型来实现:

function approx_pi{T}(n)
    ...
end

println(approx_pi{Float128}(100_000_000))

我不知道如何实现这一点。

【问题讨论】:

  • 这样做的目的是什么? tot 应该是一个整数,通常是一个 Int64。你为什么要它是一个浮点数? tot 总是小于n,这也是一个Int64。让它们漂浮没有任何目的。

标签: generics types julia


【解决方案1】:

如前所述,您可以滥用 Julia 类型系统,但这是非常不习惯的,永远不应该在实践中使用。

struct ApproxPi{T} end

function ApproxPi{T}(n) where T
    tot = zero(T) 
    for i in 1:n
        x = rand(T)
        y = rand(T)
        if x^2 + y^2 < 1
            tot+=1
        end
    end
    tot / n * 4
end

julia> ApproxPi{Float32}(100_000)
3.14144f0

【讨论】:

  • 感谢您的宝贵时间。我想遵循最佳实践。也就是说,如果您能提及参数类型的用例,我将不胜感激。我也不明白为什么必须定义一个结构,然后定义一个同名的函数。我猜这是结构的“构造函数”?如果是这样,确实是对类型系统的滥用。最后,了解惯用 Julia 结构的最佳来源是什么。
  • Julia 类型系统的最佳来源是手册。特别是,这里描述了参数类型系统:docs.julialang.org/en/v1/manual/types/#Parametric-Types 关于您的问题,您不能定义任意参数函数(即带大括号的函数),只能定义参数构造函数。所以,是的,我定义了类型和构造函数,而不是实际创建所需类型的变量,只是输出一些浮点数。
【解决方案2】:

类型在 Julia 中是一等公民,因此您可以像使用任何其他值一样将它们用作函数参数。

例如,在这种情况下,您可以简单地将所需类型指定为附加参数:

julia> function approx_pi(T, n)
           tot = zero(T)   # Better than T(0)
           for i in 1:n
               x = rand(T) # Not sure whether you want these to be of
               y = rand(T) # type T, or remain as Float64
               if x^2 + y^2 < 1
                   tot+=1
               end
           end
           tot / n * 4
       end
approx_pi (generic function with 1 method)

julia> approx_pi(BigFloat, 1_000_000)
3.141276000000000000000000000000000000000000000000000000000000000000000000000003

【讨论】:

  • 感谢您的回答。有没有办法使用approx_pi{Float64}(n)之类的语法?
  • 这样的语法看起来更像是参数类型的实例化。你可以(ab)使用这个观察来得到你想要的,但这在 Julia 中可能不会被认为是惯用的。你有什么理由要使用这种特定的语法吗?
  • 好吧,我看到创建新数组时使用了这种技术:例如:Array{Int64}(undef, 10)。在这种情况下,类型不作为参数传递。您可以向我推荐关于何时使用每种技术的最佳实践文档,否则为什么会存在参数类型?
  • Array{Int}(undef, 10) 正是它的本质:创建一个新对象。这与计算一些任意函数不同。
  • @FrançoisFévotte 再次感谢您的回复。我赞成您的回答,得到了其他人的赞赏。
【解决方案3】:

这是第三种变体,是 Andrej 答案的派生词,我相信它更惯用。也就是说,我们不是“滥用”构造函数来进行计算,而是通过显式类型构造一个多态闭包:

struct ApproxPi{T} end

function (::ApproxPi{T})(n) where T
    tot = zero(T) 
    for i in 1:n
        x = rand(T)
        y = rand(T)
        if x^2 + y^2 < 1
            tot += 1
        end
    end
    tot / n * 4
end

const approx_pi = ApproxPi{Float32}()

julia> approx_pi(100_000)
3.14144f0

如果您不是一直动态更改类型,这应该仍然实用。

【讨论】:

    猜你喜欢
    • 2023-03-09
    • 2021-06-27
    • 2019-03-05
    • 2010-10-04
    • 2020-07-04
    • 2020-10-26
    • 1970-01-01
    • 2020-09-16
    相关资源
    最近更新 更多