Julia: Déprécier les « un » ?

Créé le 2 nov. 2017  ·  46Commentaires  ·  Source: JuliaLang/julia

Maintenant que nous distinguons one et oneunit , ones(T, sz) semble être un terme impropre. Déprécier en faveur de fill(oneunit(T), sz) ? Si oui, devrions-nous aussi abandonner zeros ?

decision linear algebra stdlib

Commentaire le plus utile

Pour moi, fill(oneunit(T), sz) ressemble à une perte de lisibilité non négligeable par rapport à ones(T, sz) .

Tous les 46 commentaires

xref https://github.com/JuliaLang/julia/issues/11557#issuecomment -339776065 et inférieur, ainsi que le PR #24389 de

J'ai du travail en cours à cet effet que j'espère publier dans un jour ou deux :). Meilleur!

Pour moi, fill(oneunit(T), sz) ressemble à une perte de lisibilité non négligeable par rapport à ones(T, sz) .

Notez que vous avez rarement besoin d'écrire quelque chose d'aussi détaillé que fill(oneunit(T), sz) , car généralement un littéral ou quelque chose d'aussi compact suffit à la place de oneunit(T) . Des incantations plus courtes peuvent également devenir possibles avec des modifications futures des constructeurs de tableaux. De plus, une fois que vous êtes passé à fill , vous l'aimez, je vous assure :). Meilleur!

Nous pourrions simplement choisir si ones utilise one ou oneunit . ones et zeros doivent être considérés comme des fonctions de commodité, ce qui est acceptable tant qu'elles ont une signification claire en termes de fonctions plus générales.

Plan par triage : Revoir la semaine prochaine, en envisageant spécifiquement de déplacer ones et zeros vers la couche de compatibilité MATLAB, en faveur de fill dans la base. Meilleur!

ones et oneunits distingueraient les deux options.

Je ne sais pas ce que je ressens à propos du déplacement de cela et zeros compatibilité de fiers . Juste brièvement - quelles étaient les raisons derrière cette pensée ? (Désolé, je dois dire que le triage semble beaucoup plus efficace, mais nettement moins transparent lorsque le raisonnement n'est pas donné).

Ce n'est pas non plus un changement dont je suis ravi, mais je l'ai soulevé parce que c'est une incohérence logique. Je pense qu'il est juste de dire que ones(T, sz) implique fill(one(T), sz) mais en réalité, ce qu'il fait sous le capot est fill(oneunit(T), sz) .

Une option serait de le renommer en oneunits . Une alternative serait de faire un échange horrible : one -> algebraic_one et oneunit -> one . C'est cassant et impossible à faire via la dépréciation, c'est pourquoi je dis "horrible".

Oui, je dirais qu'ajouter oneunits et changer ones pour donner fill(one(T), ...) serait la solution évidente, non ?

Je serais bien avec ça. Par curiosité, à quoi servent les fill(one(T), sz) ? Avons-nous même besoin de ones ?

Haha :) J'allais demander - pour quoi utilisez-vous fill(oneunits(T), sz) ? (Par exemple, à quoi servirait un tableau rempli de 1 mètre, ou 1 kilogramme, ou 1 seconde ?)

Je pense que fill(one(T), sz) est utilisé pour la même raison que zeros(T, sz) , qui est d'initialiser un tableau pour une opération de type de réduction personnalisée. Un tableau z = zeros(T, sz) est prêt à avoir ses éléments ajoutés avec z[i] += x alors que o = fill(one(T), sz) est prêt à avoir ses éléments multipliés o[i] *= x . Par exemple, je pense à des situations où les éléments du tableau pourraient représenter une probabilité (relative). Dans ces deux cas, je recherche l'identité de l'opérateur + ou * respectivement (et non des générateurs additifs).

Voir aussi #23544

Haha :) J'allais demander - pour quoi utilisez-vous fill(oneunits(T), sz) ? (Par exemple, à quoi servirait un tableau rempli de 1 mètre, ou 1 kilogramme, ou 1 seconde ?)

Les variables dépendantes pour une EDO. Ce serait bizarre s'il coupait juste des unités au hasard lorsque vous demandiez un tableau de type T ?

La raison fondamentale de préférer fill est qu'un ensemble restreint d'outils puissants, orthogonaux et composables sert mieux qu'une plus grande collection d'outils ad hoc, limités et qui se chevauchent tels que ones / zeros / trues / falses . La discussion ci-dessus met en évidence ce point : alors que fill s'adapte à tous les cas d'utilisation ci-dessus sans ambiguïté et (dans la plupart des cas réels) de manière concise, le ones / zeros / trues / L'approche falses nécessite une nouvelle fonction pour chaque cas d'utilisation.

Quelques exemples pertinents de réécritures dans la base :

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

devient

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

et

2ones(Int, 2, 2, 2)

devient

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

Veuillez noter que ces incantations fill sont plus simples, plus lisibles, plus compactes et plus efficaces que leurs homologues non fill , et que base/ et test/ sont truffé d'exemples comme ceux-ci. Comme pour tout changement, la transition vers fill nécessite un ajustement mental initial. Mais après ajustement, vous constatez que vous avez plus de puissance et d'élégance à portée de main :). Meilleur!

@Sacha0 : trues / falses ne sont pas directement remplaçables par fill , mais doivent utiliser fill! avec un initialiseur BitArray . Ils ne sont pas non plus inclus dans l'ambiguïté entre one et oneunit . Par conséquent, je ne pense pas qu'ils s'intègrent du tout dans cette discussion.

Quant à ones , je suis généralement opposé à sa dépréciation de toute façon, je n'en vois pas l'avantage. L'argument selon lequel certaines expressions peuvent être écrites plus efficacement avec fill n'est pas très convaincant à mon avis, car tous ces exemples utilisent ones comme étapes intermédiaires pour faire autre chose ; mais qu'en est-il lorsque vous en voulez réellement un tableau ? Ensuite, devoir utiliser fill est plus long, moins évident et juste plus ennuyeux. J'aime mieux la proposition de base ou test peuvent être réécrites plus efficacement en utilisant fill au lieu de ones , rien ne l'empêche pour le moment.

@carlobaldassi Bien que cela soit vrai, une recherche rapide sur GitHub révèle que presque toutes les utilisations de ones devraient vraiment utiliser fill pour éviter les allocations intermédiaires...

Utiliser fill pour ces cas est logique pour moi. Notez que nous voudrons une nouvelle méthode fill(x, A) = fill!(x, copymutable(A)) pour remplacer les méthodes correspondantes de ones etc.

@TotalVerb D'après un survol rapide des 10 premières pages de cette recherche, je ne dirais pas que "presque tout" est une évaluation juste. Une "fraction importante", au mieux. Il y a beaucoup de cas d'utilisation légitimes pour ones là-bas, peut-être même la majorité (et même s'ils n'étaient que 20%, je pense que mon argument tient toujours).

(J'ai aussi des réserves sur l'argument de lisibilité, je pense qu'il est discutable d'affirmer que par exemple fill(2, 2, 2, 2) est plus lisible que 2 * ones(Int, 2, 2, 2) , ou que, disons, fill(k * 1.0, 3) serait plus lisible que k * ones(3) ; je ne suis pas du tout convaincu que ce soit simplement une question d'habitude. C'est un point mineur cependant.)

les vrais/faux ne sont pas directement remplaçables par fill, mais doivent utiliser fill! avec un initialiseur BitArray. Ils ne sont pas non plus inclus dans l'ambiguïté entre une et une unité. Par conséquent, je ne pense pas qu'ils s'intègrent du tout dans cette discussion.

En effet, trues et falses ne sont pas directement remplaçables par fill :). Au contraire, trues et falses sont des instances supplémentaires du problème général décrit ci-dessus / lié à #11557 (et que la direction récente des constructeurs de tableaux abordera, espérons-le). D'autres exemples incluent l'existence de bones , bzeros , brand , brandn et beye dans BandedMatrices.jl et les équivalents avec un d préfixe dans DistributedArrays.jl.

Quant à ceux, je suis généralement opposé à le déprécier de toute façon, je n'en vois pas l'avantage. L'argument selon lequel une expression peut être écrite plus efficacement avec fill n'est pas très convaincant à mon avis, car tous ces exemples en utilisent comme étapes intermédiaires pour faire autre chose

Ayant juste réécrit quelques centaines d'utilisations de ones en base, je peux affirmer la déclaration de @TotalVerb

une recherche rapide sur GitHub révèle que presque toutes les utilisations de celles-ci devraient vraiment utiliser le remplissage pour éviter les allocations intermédiaires ...

(Edit : bien que je dirais à peu près la moitié plutôt que presque toutes, et les réécritures appropriées peuvent être autre chose que fill .) De plus, cette expérience de réécriture m'a appris...

mais qu'en est-il lorsque vous en voulez réellement un tableau ? Ensuite, avoir à utiliser le remplissage est plus long, moins évident et juste plus ennuyeux.

... que d'autre part, fill est souvent plus court et plus simple dans ce cas: ceux requis ne sont souvent pas Float64 ( au lieu par exemple ones(Int, n...) et ones(Complex{Float64}, n...) ), auquel cas fill est plus court et plus simple en admettant un littéral (par exemple fill(1, n...) et fill(1.0 + 0im, n...) ). En termes mesurables, la branche dans laquelle j'ai réécrit les ones appels en base est ~5% plus courte en nombre de caractères de ones -> fill réécritures. Meilleur!

Pour avoir une idée objective de la façon dont ones apparaît dans la nature, j'ai collecté tous les appels ones apparaissant dans les dix premières pages d'une recherche GitHub pour ones dans le code Julia, réécrit chacun de ces appels comme il convient et a classé le changement correspondant (voir cet aperçu ), puis a réduit les données de classification au résumé suivant :

L'analyse comprenait 156 ones appels. De ces appels,

  • 84 appels (~54%) étaient des fill s ad hoc. (Par exemple, ones(100)/sqrt(100)*7 simplifie en fill(7/sqrt(100), 100) ou, mieux encore, en fill(.7, 100) . Mon préféré était kron(0.997, ones(1, J*J*s) -> fill(0.997, 1, J*J*s) .)

  • 3 appels (~2%) étaient des diffusions ad hoc. (Par exemple, A - ones(n,n) simplifie en A .- 1. .)

  • 5 appels (~3%) étaient des littéraux vectoriels ad hoc. (Par exemple, ones(1) simplifie en [1.] .)

  • 1 appel (~0,5 %) était sémantiquement une construction matricielle indésirable. (Bien que relativement rare dans la nature, ce modèle est assez courant dans test/ car nous n'avons pas de constructeur de commodité concis pour les Array non initialisés, comme par exemple <strong i="32">@test_throws</strong> DimensionMismatch BLAS.trsv(...,Vector{elty}(n+1)) contre <strong i="34">@test_throws</strong> DimensionMismatch BLAS.trsv(...,ones(elty,n+1)) .)

Les appels restants étaient sémantiquement raisonnables comme ones , bien que souvent ones soient utilisés simplement parce qu'ils sont courts, plutôt que parce que les one sont spécifiquement nécessaires. De ces appels restants,

  • 13 appels (~8%) étaient légèrement plus courts que fill . (Par exemple, ones(Int, n, n) -> fill(1, n, n) ou ones(Float64, n) -> fill(1., n) .)

  • 50 appels (~32%) étaient légèrement plus longs que fill . (Par exemple, ones(n, n) -> fill(1., n, n) .)

Dans l'ensemble, dans la nature, ~60 % des appels ones sont mieux écrits d'une autre manière, ~8% sont raisonnablement sémantiques ones et légèrement plus courts que fill , et ~32% sont raisonnablement sémantiquement ones et légèrement plus long que fill .

Une remarque supplémentaire :

Je n'ai rencontré qu'une seule instance d'un appel ones acceptant un argument de tableau, et il n'était pas clair si l'extrait englobant était du code réel. Ainsi, les méthodes ones acceptant un argument de tableau ont peu ou pas d'utilité dans la nature.

Une discussion vraiment intéressante ... est passée du côté contre au côté pour ... Également comme autre précédent de langage, R utilise rep et matrix d'une manière équivalente au fill (correspondant juste aux cas 1d et 2d) et on s'y habitue très vite -- même si je viens d'un monde de zéros/uns.

Wow, merci @Sacha0 pour cet effort !

La question se pose naturellement comme « qu'en est-il de zeros » ? Je suppose qu'il y aura beaucoup plus d'utilisation et quelques catégories d'utilisation supplémentaires (y compris des choses comme "Je ne fais tout simplement pas confiance aux tableaux non initialisés" ou "Je ne sais pas comment utiliser les constructeurs Array ").

Pour une raison quelconque (je suppose que c'est la symétrie de one et zero ), je suis quelque peu attiré par le remplacement à la fois de ones et zeros par fill , ou ni l'un ni l'autre.

Le problème avec les zéros, c'est que vous sembleriez être dans l'une de ces situations :

  1. Vous devez écraser la plupart des zéros – dans ce cas, vous feriez mieux d'utiliser une compréhension ;
  2. Vous n'avez pas besoin de remplacer la plupart des zéros – dans ce cas, vous feriez mieux d'utiliser une matrice clairsemée ;
  3. Vous avez en fait besoin d'une matrice à zéro - dans ce cas, vous feriez mieux d'utiliser 0I .

Il n'y a vraiment aucun cas d'utilisation où l'allocation d'une matrice de zéros denses est en fait une bonne idée.

C'est peut-être vrai de l'algèbre linéaire. Il n'est pas rare d'avoir besoin d'une collection initialisée à zéro dans mon travail sur les compilateurs et autres structures de données. Peut-être qu'ils sont souvent rares, mais cela ne vaut pas l'impact sur les performances de les représenter de manière compacte.

Assez juste - parfois vous ne vous souciez pas de la densité et la simplicité en vaut la peine.

Triage : résolu que nous ne conservions que les méthodes complètement non génériques, c'est-à-dire zeros(dims...) et ones(dims...) et peut-être aussi zeros(dims) et ones(dims) .

@StefanKarpinski pour les recommandations d'utilisation, cela signifie-t-il que nous recommanderions zeros(3, 3) sur fill(0.0, 3, 3) pour le code normal (quand un tableau dense est souhaité, etc.) ? Certains des détails de l'efficacité, etc. sont hors de ma portée, je pense juste à la façon dont j'enseignerais les meilleures pratiques idiomatiques à Julia pour aller de l'avant.

Cette décision me parait très surprenante, ce n'est pas si courant dans la base d'empêcher spécifiquement la généricité. Quel est le raisonnement derrière? est-ce que cette fonction vient de matlab où elle n'est pas générique (et ne fonctionne qu'avec des flottants) ?

Triage : résolu que nous ne conservions que les méthodes totalement non génériques

Quel est le raisonnement derrière?

De plus, ce problème est-il lié d'une manière ou d'une autre au problème général de la construction de tableaux qui semble être envisagé en ce moment, et si oui, en quoi cela aide-t-il ? Ou essayons-nous de réduire le nombre de méthodes exportées de Base ? (Ou tout autre chose ?)

Rédaction à venir sur https://github.com/JuliaLang/julia/issues/24595 :). Meilleur!

L'OP de 24595 a détaillé le contexte plus large de ce problème, et maintenant un suivi dans # 24595 aborde spécifiquement les constructeurs de commodité ones , zeros et fill en profondeur. La lecture du premier est précieuse pour apprécier le second. Meilleur!

Eh bien, économiser au moins le cas Float64 est mieux que rien.
Je pense que le cas des zéros entiers est également tout à fait pertinent, ce qui - je suppose - est essentiellement le point que @vtjnash faisait ici .

Il faut aussi noter que zeros n'a pas le "problème" d'autoriser l'anti-pattern 3 * ones(n) . En fait, je ne vois pas vraiment pourquoi ones et zeros devraient aller ensemble, sauf au sens large d'être des constructeurs de commodité. Il n'y a pas vraiment de « symétrie » entre ces deux-là.

Quelques commentaires supplémentaires sur l' analyse statistique , puisqu'elle semble être la base des discussions suivantes et de la rédaction de #24595. Premièrement, dix pages ne suffisent pas vraiment pour tirer des conclusions précises sur ce qui se passe dans la nature, elles peuvent au mieux donner une idée approximative. Certains fichiers proviennent directement de matlab, par exemple, comme cela ressort clairement de leur nom/style. Deuxièmement, comme je le soupçonnais, même cette analyse montre qu'environ la moitié des utilisations de ones étaient "légitimes". Troisièmement, regarder du code comme celui-ci ne dit rien sur le moment où l'écriture de 3 * ones(...) est vraiment un anti-modèle qui crée des problèmes de performances, ou c'est un morceau de code qui n'a aucune implication sur les performances (et l'écrivain peut ont décidé que c'est juste plus lisible écrit de cette façon - ce que je pense fortement n'est l'affaire de personne d'autre d'en décider autrement, dans ce cas).

En lien avec le dernier point, et je pense plus important encore, ce que vous obtenez à partir d'une recherche github ne prendra jamais en compte ce qui se passe dans le REPL des personnes effectuant un travail exploratoire/préliminaire dans Julia. C'est exactement là que les fonctions de commodité sont les plus utiles, et les enlever sans raison discernable est d'autant plus ennuyeux. Mon point étant, avoir un ensemble cohérent de primitives orthogonales qui permettent d'écrire du code générique et efficace est un grand objectif, et l'effort pour y arriver est vraiment louable ; c'est juste que tout le code n'est pas censé être un code de bibliothèque beau, générique et composable. Juste mes deux cents.

Concernant

Je pense que le cas des zéros entiers est également tout à fait pertinent, ce qui - je suppose - est essentiellement le point que @vtjnash faisait ici.

qui fait référence à

Il n'est pas rare d'avoir besoin d'une collection initialisée à zéro dans mon travail sur les compilateurs et autres structures de données. Peut-être qu'ils sont souvent rares, mais cela ne vaut pas l'impact sur les performances de les représenter de manière compacte.

Notez que fill sert aussi bien ou mieux dans ce cas : fill(0, shape...) contre zeros(Int, shape...) .

Concernant vos autres points, #24595 peut valoir la peine d'être lu :). Meilleur!

Je trouve simplement que zeros(Int, 10, 10) est plus lisible/explicite que fill(0, 10, 10) , et zeros(T, k) est meilleur que fill(zero(T), k) . Pourquoi ne pouvons-nous pas simplement avoir les deux ? Je n'accepte pas l'argument selon lequel zeros souffre du même problème d'ambiguïté que ones .

Concernant vos autres points, #24595 peut valoir la peine d'être lu

Je l'avais lu. (Je l'ai même lié.)

Je l'avais lu. (Je l'ai même lié.)

Après avoir lu le #24595 dans son intégralité et l'avoir dûment pris en considération, vous êtes alors conscient que le #24595 : (1) concerne un problème beaucoup plus large dont les constructeurs de commodité ne sont qu'une partie ; et (2) considère bien plus que l'analyse statistique publiée ci-dessus et les points sur lesquels vous vous concentrez ici.

J'apprécie que vous vous sentiez fortement à propos de ones et zeros ; votre sentiment est venu haut et fort :). En tant que tel, il est probable que notre bande passante serait mieux utilisée pour faire avancer d'autres fronts que de poursuivre cette conversation sous sa forme actuelle. Meilleur!

Y a-t-il un fill générique entrant avec le changement zeros ? zeros avait une utilisation très légitime pour la programmation générique car c'est beaucoup plus sûr que similar , donc tout DiffEq, et puis je sais que récemment Optim et NLsolve sont passés de l'allocation avec similar à zeros puisque sachant que tout est alloué pour avoir des zéros, cela arrête beaucoup de bogues. Cependant, il semble maintenant qu'il n'y aura aucune méthode pour :

zeros(X)

plus, à part :

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

parce que le fill actuel ne construit que des Array s et donc ne correspond pas au type comme le font similar ou zeros . Je sais que certaines personnes ont abusé de zeros pour allouer alors qu'elles n'auraient pas dû, mais allouer avec zeros est une chose très raisonnable à faire dans de nombreux cas. J'espère qu'un raccourci fill(0,X) ajouté pour combler ce vide.

Merci beaucoup pour le post réfléchi Chris! :) En tant que remplacement abrégé provisoire, est-ce que zero(X) fait l'affaire ?

Notez que de tels cas d'utilisation sont précisément là où l'ambiguïté dans zeros et ones peut être problématique : est-ce que zeros(X) produit un objet avec eltype(X) et rempli de eltype(X) identité additive de fill!(similar(X), 0) dire eltype(X) (peut-être pas de eltype(X) ) ? (Pour l'extension, voir #24595.)

Le concept fill(0, X) un peu de discussion dans #11557, et je suis d'accord que cela pourrait être une généralisation utile de fill . Merci et meilleur !

L'autre problème est que les tableaux avec des index non conventionnels peuvent vouloir être créés avec quelque chose comme zeros(inds...) (car le type d'index détermine le type de tableau ). Mais pour un cas 1-d, est-ce que X "le tableau auquel vous voulez ressembler" ou "les indices du tableau souhaité" ? (Après tout, AbstractUnitRange <: AbstractArray .) Concrètement, est-ce que zeros(3:5) signifie fill!(similar(3:5), 0) ou fill!(OffsetArray(Vector{Float64}(3), 3:5), 0) ?

Lien https://github.com/JuliaLang/julia/pull/24656 , qui contient une discussion supplémentaire sur les constructeurs de commodité {ones|zeros }(A::AbstractArray, ...) . Meilleur!

Je suis surpris qu'il soit considéré comme étrange d'utiliser zeros pour créer des variables de cache selon #24656. Je pense que, si zeros était réduit à près de zéro frais généraux, presque tous les cas où les gens utilisent similar devraient plutôt être zeros car cela a tendance à en corriger un certain nombre Bugs. Je pense que nous devrions encourager plus de gens à le faire car similar peut être assez dangereux, et ne pas avoir de fonction et plutôt rassembler fill! + similar rend moins évident que c'est ce que les gens devraient faire. Voici un commentaire à ce sujet dans Optim.jl :

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

Cependant, je suis d'accord avec @timholy qu'il n'est pas évident de savoir comment cela doit être interprété. Permettez-moi de vous indiquer un exemple vraiment non simple dans DiffEq.

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

MultiScaleArrays.jl a créé des tableaux abstraits qui sont des structures de type graphique récursives qui peuvent être utilisées dans les solveurs diffeq (et je pense que cela peut être compatible avec Optim.jl et NLsolve.jl maintenant ?). C'est une belle commodité pour les modèles biologiques entre autres. Lorsque nous le lançons dans le solveur ODE, une question se pose : que devrions-nous faire des tableaux de cache ? Dans certains cas, il est important que l'utilisateur récupère le tableau qu'il voulait car il apparaîtra dans sa fonction ODE f(t,u,du) et il voudra le traiter en conséquence. Cependant, dans d'autres cas, cela n'apparaît que comme quelque chose qui est diffusé en interne. Il existe donc deux types différents de variables de cache.

Pour gérer cela, jetez un œil au cache de l'un des algorithmes :

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

Ici, rate_prototype = similar(u,first(u)/t,indices(u) est similaire mais avec un eltype potentiellement différent pour les unités. Mais notez qu'il existe ici deux types distincts : similar(u) vs similar(u,indices(u)) . J'ai interprété ceux-ci comme signifiant "faire correspondre le type et la forme" plutôt que "faire correspondre la forme et l'eltype, mais n'a pas besoin d'être du même type". Donc pour un AbstractMultiScaleArray , le premier créera un autre AbstractMultiScaleArray tandis que l'autre, pour la vitesse puisqu'il n'est pas vu par l'utilisateur, créera juste un Array du Taille. Celles-ci sont ensuite étendues à similar(u,T) et similar(u,T,indices(u)) .

C'est peut-être juste un jeu de mots sur ce qui existe déjà, mais je pense que c'est une distinction importante. Lorsque vous effectuez une programmation générique, vous disposez de deux caches distincts : des caches destinés à l'utilisateur que vous souhaitez faire correspondre les types à leurs attentes et des caches internes qui sont simplement utilisés par l'algorithme et que vous souhaitez obtenir le plus de vitesse possible.

Notez que ceux-ci utilisent similar parce que j'étais trop paresseux pour en proposer une version zeros . J'ai en fait un endroit séparé qui peut mettre à zéro certains de ces tableaux, car si l'utilisateur ne définit que du dans son calcul de dérivé f(t,u,du) , il a tendance à penser implicitement "ce que je ne définis pas signifie zero", ce qui n'est vrai que lorsqu'il a été alloué avec zeros , donc j'essaie de pré-allouer en utilisant zeros autant que possible (le même problème se pose dans NLsolve.jl pour cela) .

Espérons que cette explication n'était pas trop confuse à suivre. Dans tous mes cas, je peux simplement passer à similar suivi de fill! , mais je sais que certains paquets ne le feront pas et ce sera une source de bogues.

Intéressant. Vous avez raison de dire que similar(A, inds) peut créer un type différent de similar(A) , mais en général, j'ai toujours pensé qu'il créait probablement le même type mais avec des indices différents. Par exemple, si vous aviez besoin d'un cache à 1 dimension pour une opération par colonne sur un objet à 2 dimensions, j'utiliserais similar(A, first(inds)) . (Bien sûr, il s'agit d' un type différent car la dimensionnalité est un paramètre de type, mais il peut s'agir du même type de conteneur abstrait.) Vous pouvez également l'utiliser pour créer un cache 5x5 d'une petite tuile, etc.

Dans l'ensemble, cela semble être un problème difficile. C'est un peu tard dans le jeu, mais faut-il introduire same ? Il pourrait avoir les mêmes arguments que similar , mais le contrat serait qu'il serait nécessaire de retourner le même conteneur abstrait.

Je pourrais prendre en charge une forme à un argument de same , mais même cela est délicat - notez que same(a) ne pourrait pas renvoyer le même type de tableau si a ne prend pas en charge setindex! car same et similar ne sont utiles que si vous allez écrire dans le tableau par la suite. Nous pourrions en faire une erreur pour immuable a , mais en tant qu'interface pour AbstractArray cela semble inutile (et peut-être inutile) pour créer un code générique correct.

De même, nous ne pouvons pas supposer que chaque AbstractArray peut prendre en charge différents eltypes ou indices. Pour moi, avoir une forme à deux ou trois arguments de same ne ferait qu'introduire des erreurs d'exécution dans un tas d'endroits tout en donnant aux gens un faux sentiment de sécurité que leur code générique fonctionnera bien pour n'importe quel AbstractArray entrée, quand ce n'est pas le cas.

Mais pour un cas 1-d, X est-il "le tableau auquel vous voulez être similaire" ou "les indices du tableau souhaité" ?

C'est une autre raison pour laquelle je suis en faveur du retour de keys un conteneur avec des indices et des valeurs identiques, puis d'en faire une exigence de similar (à moins que vous ne fournissiez un (des) entier(s) dans auquel cas Base.OneTo (CartésianRange) est supposé).

Cette discussion s'oriente vers le #18161, et devrait peut-être continuer là :).

Le triage le plus récent penchait pour ne garder que ones .

Est-ce que ça fait mal de les garder? Je pense que cela aide les gens qui viennent de numpy à se sentir chez eux chez Julia.

Fermeture puisque nous gardons ones et zeros .

Cette page vous a été utile?
0 / 5 - 0 notes