arrays types julia-lang multiple-dispatch

arrays - Cómo escribir el código "bueno" de Julia cuando se trata de varios tipos y matrices(despacho múltiple)



types julia-lang (1)

Soy nuevo en Julia y, dados mis orígenes en Matlab, tengo algunas dificultades para determinar cómo escribir un código "bueno" de Julia que aproveche el despacho múltiple y el sistema de tipos de Julia.

Considere el caso en el que tengo una función que proporciona el cuadrado de un Float64 . Podría escribir esto como:

function mysquare(x::Float64) return(x^2); end

A veces, quiero cuadrar todos los Float64 en una matriz unidimensional, pero no quiero escribir un bucle sobre mysquare cada vez, así que utilizo el envío múltiple y agrego lo siguiente:

function mysquare(x::Array{Float64, 1}) y = Array(Float64, length(x)); for k = 1:length(x) y[k] = x[k]^2; end return(y); end

Pero ahora a veces estoy trabajando con Int64 , así que escribo dos funciones más que aprovechan el despacho múltiple:

function mysquare(x::Int64) return(x^2); end function mysquare(x::Array{Int64, 1}) y = Array(Float64, length(x)); for k = 1:length(x) y[k] = x[k]^2; end return(y); end

¿Es esto correcto? ¿O hay una forma más ideológica de lidiar con esta situación? ¿Debo usar parámetros de tipo como este?

function mysquare{T<:Number}(x::T) return(x^2); end function mysquare{T<:Number}(x::Array{T, 1}) y = Array(Float64, length(x)); for k = 1:length(x) y[k] = x[k]^2; end return(y); end

Esto se siente sensato, pero ¿se ejecutará mi código tan rápido como en el caso en que evito los tipos paramétricos?

En resumen, mi pregunta tiene dos partes:

  1. Si el código rápido es importante para mí, ¿debo usar tipos paramétricos como se describe anteriormente, o debo escribir múltiples versiones para diferentes tipos de concreto? ¿O debería hacer algo completamente diferente?

  2. Cuando quiero una función que opera tanto en arreglos como en escalares, ¿es una buena práctica escribir dos versiones de la función, una para el escalar y otra para la matriz? ¿O debería estar haciendo algo completamente diferente?

Finalmente, señale cualquier otro problema que pueda pensar en el código anterior ya que mi objetivo final aquí es escribir un buen código Julia.

OP UPDATE: Tenga en cuenta que en la última versión de Julia (v0.5), el enfoque idiomático para responder a esta pregunta es simplemente definir mysquare(x::Number) = x^2 . El caso vectorizado se cubre utilizando transmisión automática, es decir, x = randn(5) ; mysquare.(x) x = randn(5) ; mysquare.(x)


Julia compila una versión específica de su función para cada conjunto de entradas según sea necesario. Por lo tanto, para responder a la parte 1, no hay diferencia de rendimiento. La forma paramétrica es el camino a seguir.

En cuanto a la parte 2, podría ser una buena idea en algunos casos escribir una versión separada (a veces por motivos de rendimiento, por ejemplo, para evitar una copia). En su caso, sin embargo, puede usar la macro incorporada @vectorize_1arg para generar automáticamente la versión del arreglo, por ejemplo:

function mysquare{T<:Number}(x::T) return(x^2) end @vectorize_1arg Number mysquare println(mysquare([1,2,3]))

En cuanto al estilo general, no use punto y coma, y mysquare(x::Number) = x^2 es mucho más corto.

En cuanto a su mysquare vectorizada, considere el caso en el que T es un BigFloat . Sin embargo, su matriz de salida es Float64 . Una forma de manejar esto sería cambiarlo a

function mysquare{T<:Number}(x::Array{T,1}) n = length(x) y = Array(T, n) for k = 1:n @inbounds y[k] = x[k]^2 end return y end

donde agregué la macro @inbounds para aumentar la velocidad porque no es necesario que verifique la violación encuadernada cada vez; sabemos las longitudes. Esta función aún podría tener problemas en el caso de que el tipo de x[k]^2 no sea T Una versión aún más defensiva sería tal vez

function mysquare{T<:Number}(x::Array{T,1}) n = length(x) y = Array(typeof(one(T)^2), n) for k = 1:n @inbounds y[k] = x[k]^2 end return y end

donde one(T) daría 1 si T es un Int , y 1.0 si T es un Float64 , y así sucesivamente. Estas consideraciones solo importan si quiere crear un código de biblioteca hiper robusto. Si realmente solo va a tratar con Float64 s o cosas que pueden promocionarse a Float64 s, entonces no es un problema. Parece un trabajo duro, pero el poder es asombroso. Siempre puede conformarse con el rendimiento similar al de Python e ignorar toda la información de tipo.