Julia: ¿Dejar de lado "unos"?

Creado en 2 nov. 2017  ·  46Comentarios  ·  Fuente: JuliaLang/julia

Ahora que distinguimos one y oneunit , ones(T, sz) parece un nombre inapropiado. ¿Depreciar a favor de fill(oneunit(T), sz) ? Si es así, ¿deberíamos deshacernos de zeros también?

decision linear algebra stdlib

Comentario más útil

Para mí, fill(oneunit(T), sz) parece una pérdida no trivial en legibilidad en comparación con ones(T, sz) .

Todos 46 comentarios

xref https://github.com/JuliaLang/julia/issues/11557#issuecomment -339776065 y más abajo, y también el PR # 24389 de @ Sacha0

Tengo un trabajo en progreso en este sentido que espero publicar en los próximos días :). ¡Mejor!

Para mí, fill(oneunit(T), sz) parece una pérdida no trivial en legibilidad en comparación con ones(T, sz) .

Tenga en cuenta que rara vez necesita escribir algo tan detallado como fill(oneunit(T), sz) , ya que normalmente un literal o algo similarmente compacto es suficiente en lugar de oneunit(T) . Los encantamientos más cortos también pueden ser posibles con cambios futuros en los constructores de matrices. Además, una vez que hayas hecho la transición a fill , te gustará, te lo aseguro :). ¡Mejor!

Podríamos elegir si ones usa one o oneunit . ones y zeros deben considerarse funciones de conveniencia, lo cual está bien siempre que tengan significados claros en términos de funciones más generales.

Plan por triaje: revise la próxima semana, considerando específicamente mover ones y zeros a la capa de compatibilidad de MATLAB, a favor de fill en la base. ¡Mejor!

ones y oneunits distinguirían las dos opciones.

No estoy seguro de cómo me siento acerca de mover esto y zeros a la compatibilidad con MATLAB. El hecho de que Mathworks lo haya inventado no significa que no sea una buena API de conveniencia de la que podamos estar orgullosos . Solo brevemente: ¿cuáles fueron las razones detrás de este pensamiento? (Lo siento, tengo que decir que el triaje parece mucho más eficiente, pero significativamente menos transparente cuando no se da el razonamiento).

Tampoco es un cambio que me entusiasme, pero lo planteé porque es una inconsistencia lógica. Creo que es justo decir que ones(T, sz) implica fill(one(T), sz) pero realmente lo que hace debajo del capó es fill(oneunit(T), sz) .

Una opción sería cambiarle el nombre a oneunits . Una alternativa sería hacer un intercambio horrible: one -> algebraic_one y oneunit -> one . Esto está roto y no es posible hacerlo a través de la desaprobación, por lo que digo "horrible".

Sí, diría que agregar oneunits y cambiar ones para dar fill(one(T), ...) sería la solución obvia para eso, ¿no?

Estaría bien con eso. Por curiosidad, ¿cuáles son los usos de fill(one(T), sz) ? ¿Incluso necesitamos ones en absoluto?

Jaja :) Iba a preguntar: ¿para qué usas fill(oneunits(T), sz) ? (Por ejemplo, ¿para qué se usaría una matriz llena de 1 metro, 1 kilogramo o 1 segundo?)

Creo que fill(one(T), sz) se usa por la misma razón que zeros(T, sz) , que es para inicializar una matriz para una operación de tipo de reducción personalizada. Una matriz z = zeros(T, sz) está lista para que se le agreguen sus elementos con z[i] += x mientras que o = fill(one(T), sz) está lista para que sus elementos se multipliquen o[i] *= x . Por ejemplo, estoy pensando en situaciones en las que los elementos de la matriz pueden representar una probabilidad (relativa). En estos dos casos, estoy buscando la identidad del operador + o * respectivamente (y no generadores aditivos).

Véase también # 23544

Jaja :) Iba a preguntar: ¿para qué usas fill (oneunits (T), sz)? (Por ejemplo, ¿para qué se usaría una matriz llena de 1 metro, 1 kilogramo o 1 segundo?)

Las variables dependientes de una EDO. Sería extraño si simplemente se cortaran unidades al azar cuando solicitara una matriz del tipo T ?

La razón fundamental para preferir fill es que un conjunto ajustado de herramientas potentes, ortogonales y componibles sirve mejor que una colección más grande de herramientas ad hoc, limitadas y superpuestas como ones / zeros / trues / falses . La discusión anterior destaca este punto: mientras que fill acomoda todos los casos de uso anteriores de manera inequívoca y (en la mayoría de los usos reales) de manera concisa, el ones / zeros / trues El enfoque / falses requiere una nueva función para cada caso de uso.

Un par de ejemplos relevantes de reescrituras en base:

complex.(ones(size(Ac, 1)), ones(size(Ac, 1)))

se convierte en

fill(1.0 + 1.0im, size(Ac, 1))

y

2ones(Int, 2, 2, 2)

se convierte en

fill(2, (2, 2, 2)) # or fill(2, 2, 2, 2) if you prefer

Tenga en cuenta que estos encantamientos fill son más simples, más legibles, más compactos y más eficientes que sus contrapartes que no son fill , y base/ y test/ son plagado de ejemplos como estos. Como con todos los cambios, la transición a fill requiere un ajuste mental inicial. Pero después del ajuste, descubres que tienes más poder y elegancia al alcance de tu mano :). ¡Mejor!

@ Sacha0 : trues / falses no son directamente reemplazables por fill , pero necesitan usar fill! con un inicializador BitArray . Tampoco se incluyen en la ambigüedad entre one y oneunit . Por tanto, no creo que encajen en absoluto en esta discusión.

En cuanto a ones , generalmente me opongo a desaprobarlo de todos modos, no veo el beneficio. El argumento de que alguna expresión se puede escribir de manera más eficaz con fill no es muy convincente en mi opinión, ya que todos estos ejemplos usan ones como pasos intermedios para hacer otra cosa; pero ¿qué pasa cuando realmente quieres una variedad de unos? Entonces, tener que usar fill es más largo, menos obvio y simplemente más molesto. Me gusta más la propuesta de base o test se pueden reescribir de manera más efectiva usando fill lugar de ones no hay nada que lo impida en este momento.

@carlobaldassi Si bien eso es cierto, una búsqueda rápida en GitHub revela que casi todos los usos de ones realmente deberían usar fill para evitar asignaciones intermedias ...

Usar fill para estos casos tiene sentido para mí. Tenga en cuenta que querremos un nuevo método fill(x, A) = fill!(x, copymutable(A)) para reemplazar los métodos correspondientes de ones etc.

@TotalVerb De un ones allí, tal vez incluso la mayoría (e incluso si fueran solo un 20%, creo que mi argumento sigue en pie).

(De hecho, también tengo reservas sobre el argumento de legibilidad, creo que es cuestionable afirmar que, por ejemplo, fill(2, 2, 2, 2) es más legible que 2 * ones(Int, 2, 2, 2) , o que, digamos, fill(k * 1.0, 3) sería más legible que k * ones(3) ; no estoy del todo convencido de que sea simplemente una cuestión de hábito. Sin embargo, este es un punto menor).

verdades / falsas no se pueden reemplazar directamente por relleno, ¡pero necesitan usar relleno! con un inicializador BitArray. Tampoco se incluyen en la ambigüedad entre una y una unidad. Por tanto, no creo que encajen en absoluto en esta discusión.

De hecho, trues y falses no se pueden reemplazar directamente por fill :). Más bien, trues y falses son instancias adicionales del problema general descrito anteriormente / relacionado con # 11557 (y que la dirección reciente de los constructores de matrices, con suerte, abordará). Otros ejemplos incluyen la existencia de, por ejemplo, bones , bzeros , brand , brandn y beye en BandedMatrices.jl y equivalentes con un d prefijo en DistributedArrays.jl.

En cuanto a algunos, generalmente me opongo a desaprobarlos de todos modos, no veo el beneficio. El argumento de que alguna expresión se puede escribir de manera más efectiva con relleno no es muy convincente en mi opinión, ya que todos estos ejemplos los usan como pasos intermedios para hacer otra cosa.

Habiendo reescrito algunos cientos de usos de ones en la base, puedo afirmar la declaración de @TotalVerb

una búsqueda rápida en GitHub revela que casi todos los usos de one realmente deberían usar fill para evitar asignaciones intermedias ...

(Editar: aunque diría que aproximadamente la mitad en lugar de casi todos, y las reescrituras apropiadas pueden ser algo diferente a fill ). Además, esa experiencia de reescritura me ha enseñado ...

pero ¿qué pasa cuando realmente quieres una variedad de unos? Entonces, tener que usar el relleno es más largo, menos obvio y más molesto.

... que, por otro lado, fill es frecuentemente más corto y más simple en ese caso: los solicitados frecuentemente no son Float64 (en su lugar, por ejemplo, ones(Int, n...) y ones(Complex{Float64}, n...) ), en cuyo caso fill es más corto y simple al admitir un literal (por ejemplo, fill(1, n...) y fill(1.0 + 0im, n...) ). En términos mensurables, la rama en la que he estado reescribiendo ones llamadas en la base es ~ 5% más corta por recuento de caracteres desde ones -> fill reescribe. ¡Mejor!

Para tener una idea objetiva de cómo ones aparece en la naturaleza, recopilé todas las llamadas ones aparecen en las primeras diez páginas de una búsqueda de GitHub para ones en el código de Julia, reescribí cada llamada según corresponda y clasificó el cambio correspondiente (ver esta esencia ), y luego redujo los datos de clasificación al siguiente resumen:

El análisis incluyó 156 ones llamadas. De esas llamadas

  • 84 llamadas (~ 54%) fueron ad hoc fill s. (Por ejemplo, ones(100)/sqrt(100)*7 simplifica a fill(7/sqrt(100), 100) o, mejor aún, fill(.7, 100) . Mi favorito era kron(0.997, ones(1, J*J*s) -> fill(0.997, 1, J*J*s) .)

  • 3 llamadas (~ 2%) fueron transmisiones ad hoc. (Por ejemplo, A - ones(n,n) simplifica a A .- 1. .)

  • 5 llamadas (~ 3%) fueron literales vectoriales ad hoc. (Por ejemplo, ones(1) simplifica a [1.] .)

  • 1 llamada (~ 0,5%) era semánticamente una construcción de matriz basura. (Aunque es relativamente poco común en la naturaleza, este patrón es bastante común en test/ porque no tenemos un constructor de conveniencia conciso para Array s no inicializados, como en, por ejemplo, <strong i="32">@test_throws</strong> DimensionMismatch BLAS.trsv(...,Vector{elty}(n+1)) versus <strong i="34">@test_throws</strong> DimensionMismatch BLAS.trsv(...,ones(elty,n+1)) .)

Las llamadas restantes eran semánticamente razonables como ones , aunque a menudo ones usa simplemente porque es corto, en lugar de porque one s específicamente sean necesarios. De esas llamadas restantes,

  • 13 llamadas (~ 8%) fueron un poco más cortas como fill . (Por ejemplo, ones(Int, n, n) -> fill(1, n, n) o ones(Float64, n) -> fill(1., n) .)

  • 50 llamadas (~ 32%) fueron un poco más largas como fill . (Por ejemplo, ones(n, n) -> fill(1., n, n) .)

En general, ~ 60% de las llamadas ones están mejor escritas de otra manera, ~ 8% son razonablemente semánticamente ones y un poco más cortas como fill , y ~ 32% son razonablemente semánticamente ones y un poco más largo como fill .

Una observación adicional:

Encontré solo una instancia de una llamada ones aceptaba un argumento de matriz, y no estaba claro si el fragmento adjunto era código real. Entonces, los métodos ones que aceptan un argumento de matriz tienen poco o ningún uso en la naturaleza.

Discusión realmente interesante ... pasó de estar del lado contrario al lado de ... También como otro precedente de lenguaje, R usa rep y matrix de una manera que es equivalente a fill (solo corresponde a los casos 1d y 2d) y te acostumbras muy rápidamente, aunque vengo de un mundo de ceros / unos.

¡Vaya, gracias @ Sacha0 por poner ese esfuerzo!

La pregunta surge naturalmente como "¿qué pasa con zeros "? Supongo que habrá un uso significativamente mayor y algunas categorías más de uso (incluidas cosas como "Simplemente no confío en las matrices no inicializadas" o "No sé cómo usar los constructores Array ").

Por la razón que sea (supongo que es la simetría de one y zero ), me atrae un poco reemplazar tanto ones como zeros con fill , o ninguno.

Lo que pasa con los ceros es que parecería estar en una de estas situaciones:

  1. Debe sobrescribir la mayoría de los ceros; en este caso, es mejor usar una comprensión;
  2. No es necesario reemplazar la mayoría de los ceros; en este caso, es mejor usar una matriz dispersa;
  3. En realidad, necesita una matriz de cero; en este caso, es mejor que use 0I .

Realmente no hay un caso de uso en el que asignar una matriz de ceros densos sea realmente una buena idea.

Eso es quizás cierto en el álgebra lineal. No es raro necesitar una colección inicializada en cero en mi trabajo sobre compiladores y otras estructuras de datos. Tal vez a menudo sean escasos, pero no vale la pena el impacto en el rendimiento para representarlos de manera compacta.

Es bastante justo: a veces no te importa la densidad y la simplicidad lo vale.

Triaje: resolvió que mantenemos solo los métodos completamente no genéricos, es decir, zeros(dims...) y ones(dims...) y tal vez zeros(dims) y ones(dims) también.

@StefanKarpinski para recomendaciones de uso, ¿eso significa que recomendaríamos zeros(3, 3) sobre fill(0.0, 3, 3) para código normal (cuando se desea una matriz densa, etc., etc.)? Algunos de los detalles de la eficiencia, etc. están fuera de mi alcance, solo estoy pensando en cómo enseñaría las mejores prácticas idiomáticas en julia en el futuro.

Esta decisión me parece muy sorprendente, no es tan común en la base para prevenir específicamente la genéricaidad. ¿Cuál es el razonamiento detrás? ¿Es que esta función proviene de matlab donde no es genérica (y funciona solo con flotantes)?

Triaje: resolvió que mantenemos solo los métodos completamente no genéricos

¿Cuál es el razonamiento detrás?

Además de esto, ¿este problema está relacionado de alguna manera con el problema general de construir matrices que parece estar siendo considerado en este momento? Y si es así, ¿cómo ayuda esto? ¿O estamos tratando de reducir la cantidad de métodos exportados de Base ? (¿O algo completamente diferente?)

Escritura próximamente en https://github.com/JuliaLang/julia/issues/24595 :). ¡Mejor!

El OP de 24595 detalló el contexto más amplio de este problema, y ​​ahora un seguimiento en # 24595 aborda específicamente los constructores de conveniencia ones , zeros y fill en profundidad. Leer el primero es valioso para apreciar el segundo. ¡Mejor!

Bueno, ahorrar al menos el caso Float64 es mejor que nada.
Creo que el caso de los ceros enteros también es bastante relevante, que, supongo, es básicamente el punto que @vtjnash estaba haciendo aquí .

También debe tenerse en cuenta que zeros no tiene el "problema" de permitir el antipatrón 3 * ones(n) . De hecho, realmente no veo por qué ones y zeros deberían ir juntos, excepto en el sentido amplio de ser constructores de conveniencia. No existe una "simetría" real entre esos dos.

Un par de comentarios adicionales sobre el análisis estadístico , ya que parece ser la base de las siguientes discusiones y la redacción de # 24595. Primero, diez páginas no son realmente suficientes para obtener conclusiones detalladas de lo que está sucediendo en la naturaleza, pueden dar una idea aproximada en el mejor de los casos. Algunos archivos provienen directamente de matlab, por ejemplo, como queda claro por su nombre / estilo. En segundo lugar, como sospechaba, incluso ese análisis muestra que aproximadamente la mitad de los usos de ones allí eran "legítimos". En tercer lugar, mirar un código como este no dice nada sobre cuándo escribir 3 * ones(...) es realmente un anti-patrón que crea problemas de rendimiento, o es un fragmento de código que no tiene ninguna implicación de rendimiento (y el escritor puede he decidido que es más legible escrito de esa manera, que creo firmemente que no es asunto de nadie más decidir lo contrario, en ese caso).

En relación con el último punto, y creo que lo más importante, lo que puedes ver en una búsqueda en github nunca tendrá en cuenta lo que está sucediendo en el REPL de las personas que realizan trabajos exploratorios / preliminares en Julia. Que es exactamente donde las funciones de conveniencia son más útiles, y eliminarlas sin ninguna razón discernible es correspondientemente más molesto. Mi punto es que tener un conjunto consistente de primitivas ortogonales que permitan escribir código genérico y eficiente es un gran objetivo, y el esfuerzo para lograrlo es verdaderamente encomiable; es solo que no se supone que todo el código sea un código de biblioteca hermoso, genérico y componible. Solo mis dos centavos.

Con respecto a

Creo que el caso de los ceros enteros también es bastante relevante, que, supongo, es básicamente el punto que @vtjnash estaba haciendo aquí.

que se refiere a

No es raro necesitar una colección inicializada en cero en mi trabajo sobre compiladores y otras estructuras de datos. Tal vez a menudo sean escasos, pero no vale la pena el impacto en el rendimiento para representarlos de manera compacta.

Tenga en cuenta que fill sirve igual o mejor en ese caso: fill(0, shape...) versus zeros(Int, shape...) .

En cuanto a sus otros puntos, vale la pena leer # 24595 :). ¡Mejor!

Simplemente encuentro que zeros(Int, 10, 10) es más legible / explícito que fill(0, 10, 10) , y zeros(T, k) es mejor que fill(zero(T), k) . ¿Por qué no podemos tener ambos? No compro el argumento de que zeros sufre el mismo problema de ambigüedad que ones .

Con respecto a sus otros puntos, vale la pena leer # 24595

Lo había leído. (Incluso lo vinculé).

Lo había leído. (Incluso lo vinculé).

Después de leer el # 24595 en su totalidad y darle la debida consideración, entonces es consciente de que el # 24595: (1) se refiere a un tema mucho más amplio del cual los constructores de conveniencia son solo una parte; y (2) considera mucho más que el análisis estadístico publicado anteriormente y los puntos en los que se centra aquí.

Aprecio que se sienta fuertemente acerca de ones y zeros ; su sentimiento ha llegado alto y claro :). Como tal, es probable que nuestro ancho de banda se gaste mejor en impulsar otros frentes hacia adelante que en continuar esta conversación en su forma actual. ¡Mejor!

¿Hay un fill genérico entrante junto con el cambio zeros ? zeros tuvo un uso muy legítimo para la programación genérica, ya que es mucho más seguro que similar , por lo que todo DiffEq, y luego sé que recientemente Optim y NLsolve pasaron de asignar con similar a zeros ya que saber que todo está asignado para tener ceros detiene muchos errores. Sin embargo, ahora parece que no habrá ningún método para:

zeros(X)

más, aparte de:

similar(X); fill!(X,0)

porque el actual fill solo construye Array s y, por lo tanto, no coincide con el tipo como lo hace similar o zeros . Sé que algunas personas hicieron zeros mal uso de zeros es algo muy razonable en muchos casos. Espero que se agregue una abreviatura fill(0,X) para llenar este vacío.

¡Muchas gracias por la reflexiva publicación Chris! :) Como reemplazo interino de la taquigrafía, ¿ zero(X) hace el truco?

Tenga en cuenta que tales casos de uso son precisamente donde la ambigüedad en zeros y ones puede ser problemática: ¿ zeros(X) produce un objeto con eltype(X) y se llena con eltype(X) Identidad aditiva de fill!(similar(X), 0) ), o en su lugar, un objeto relleno con un cero multiplicativo para eltype(X) (posiblemente no de eltype(X) )? (Para ampliar, consulte # 24595).

El concepto fill(0, X) ve un poco de discusión en # 11557, y estoy de acuerdo en que podría ser una generalización útil de fill . ¡Gracias y mejor!

El otro problema es que las matrices con índices no convencionales pueden querer crearse con algo como zeros(inds...) (porque el tipo de índice determina el tipo de matriz ). Pero para un caso 1-d, ¿es X "la matriz a la que desea ser similar" o "los índices de la matriz deseada"? (Después de todo, AbstractUnitRange <: AbstractArray .) Concretamente, ¿ zeros(3:5) significa fill!(similar(3:5), 0) o fill!(OffsetArray(Vector{Float64}(3), 3:5), 0) ?

Vinculando https://github.com/JuliaLang/julia/pull/24656 , que contiene una discusión adicional sobre los constructores de conveniencia {ones|zeros }(A::AbstractArray, ...) . ¡Mejor!

Me sorprende que se considere extraño usar zeros para crear variables de caché de acuerdo con # 24656. Yo pensaría que, si zeros se redujera a casi cero gastos generales, casi cualquier caso en el que las personas usen similar debería ser zeros ya que eso tiende a solucionar bastantes insectos. Creo que deberíamos alentar a más personas a hacer esto ya que similar puede ser bastante inseguro, y no tener una función y, en su lugar, juntar fill! + similar hace que sea menos obvio que es lo que la gente debería estar haciendo. Aquí hay un comentario sobre esta aparición en Optim.jl:

https://github.com/JuliaNLSolvers/NLsolve.jl/issues/89#issuecomment -294585960

Sin embargo, estoy de acuerdo con @timholy en que no es obvio cómo debe interpretarse. Permítanme señalarles un ejemplo realmente no simple en DiffEq.

https://github.com/JuliaDiffEq/MultiScaleArrays.jl

MultiScaleArrays.jl creó matrices abstractas que son estructuras recursivas similares a gráficos que se pueden usar en los solucionadores de diffeq (¿y creo que ahora puede ser compatible con Optim.jl y NLsolve.jl?). Es una buena conveniencia para los modelos biológicos, entre otras cosas. Cuando lo lanzamos al solucionador de ODE, surge una pregunta: ¿qué deberíamos hacer con las matrices de caché? En algunos casos, es importante que el usuario recupere la matriz que deseaba, ya que aparecerá en su función ODE f(t,u,du) y querrán tratarla en consecuencia. Sin embargo, en otros casos, solo aparece como algo contra lo que se transmite internamente. Entonces, hay dos tipos diferentes de variables de caché.

Para manejar esto, eche un vistazo a la caché de uno de los algoritmos:

https://github.com/JuliaDiffEq/OrdinaryDiffEq.jl/blob/master/src/caches/low_order_rk_caches.jl#L224 -L234

Aquí, rate_prototype = similar(u,first(u)/t,indices(u) es similar pero con eltype potencialmente diferente para las unidades. Pero observe que aquí hay dos tipos separados: similar(u) vs similar(u,indices(u)) . He interpretado que esos significan "coincidir con el tipo y la forma" frente a "coincidir con la forma y el tipo, pero no es necesario que sea del mismo tipo". Entonces, para un AbstractMultiScaleArray , el primero creará otro AbstractMultiScaleArray mientras que el otro, por velocidad, ya que el usuario no lo ve, simplemente creará un Array del apropiado Talla. Esto se extiende luego a similar(u,T) y similar(u,T,indices(u)) .

Tal vez sea solo un juego de palabras con lo que ya existe, pero creo que esta es una distinción importante. Al hacer programación genérica, tiene dos cachés separados: los cachés de cara al usuario que desea que coincidan con los tipos para que coincidan con sus expectativas, y los cachés internos que acaba de usar el algoritmo y desea la mayor velocidad posible.

Tenga en cuenta que estos están usando similar porque era demasiado vago para pensar en una versión zeros . De hecho, tengo un lugar separado que puede poner a cero algunas de estas matrices porque si el usuario solo establece du en su cálculo derivado de f(t,u,du) , tienden a pensar implícitamente "lo que no establezco significa zero ", que solo es cierto cuando se asignó con zeros , así que trato de preasignar usando zeros tanto como sea posible (el mismo problema surge en NLsolve.jl para esto) .

Con suerte, esa explicación no fue demasiado confusa para seguir. En todos mis casos, puedo cambiar a similar seguido de fill! , pero sé que algunos paquetes no lo harán y eso será una fuente de errores.

Interesante. Tienes razón en que similar(A, inds) podría crear un tipo diferente de similar(A) , pero en general siempre pensé que era probable que creara el mismo tipo pero con índices diferentes. Por ejemplo, si necesitara una caché unidimensional para una operación de columna en un objeto 2-d, usaría similar(A, first(inds)) . (Por supuesto, es un tipo diferente porque la dimensionalidad es un parámetro de tipo, pero podría ser el mismo tipo de contenedor abstracto). También puede usarlo para crear una caché de 5x5 de un mosaico pequeño, etc.

En general, esto parece ser un problema desafiante. Es un poco tarde en el juego, pero ¿deberíamos introducir same ? Podría tener los mismos argumentos que similar , pero el contrato sería que se requeriría devolver el mismo contenedor abstracto.

Podría admitir una forma de un argumento de same , pero incluso esto es complicado; tenga en cuenta que same(a) no podría devolver el mismo tipo de matriz si a no admite setindex! porque same y similar solo son útiles si luego vas a escribir en la matriz. Podríamos hacer de esto un error para a inmutables, pero como interfaz para AbstractArray esto parece innecesario (y tal vez inútil) para hacer un código genérico correcto.

De manera similar, no podemos asumir que cada AbstractArray puede admitir diferentes eltypes o índices. Para mí, tener una forma de dos o tres argumentos de same solo introduciría errores en tiempo de ejecución en un montón de lugares mientras les daría a las personas una falsa sensación de seguridad de que su código genérico funcionará bien para cualquier AbstractArray entrada, cuando no es así.

Pero para un caso 1-d, ¿es X "la matriz a la que desea que sea similar" o "los índices de la matriz deseada"?

Esta es otra razón por la que estoy a favor de que keys devuelva un contenedor con índices y valores idénticos, y luego haga que esto sea un requisito de similar (a menos que proporcione uno (algunos) enteros en cuyo caso se asume Base.OneTo (CartesianRange)).

Esta discusión está virando hacia # 18161, y quizás debería continuar ahí :).

El triaje más reciente se inclinaba hacia mantener ones .

¿Duele conservarlos? Creo que ayuda a las personas que vienen de Numpy a sentirse como en casa en Julia.

Cerrando porque nos quedamos ones y zeros .

¿Fue útil esta página
0 / 5 - 0 calificaciones