Jetzt, da wir one
und oneunit
, scheint ones(T, sz)
eine falsche Bezeichnung zu sein. Ablehnen zu Gunsten von fill(oneunit(T), sz)
? Wenn ja, sollten wir auch zeros
?
xref https://github.com/JuliaLang/julia/issues/11557#issuecomment -339776065 und darunter, und auch @Sacha0 's PR #24389
Ich habe diesbezüglich Arbeiten im Gange, die ich hoffentlich in den nächsten ein oder zwei Tagen veröffentlichen kann :). Am besten!
Für mich sieht fill(oneunit(T), sz)
im Vergleich zu ones(T, sz)
wie ein nicht trivialer Verlust an Lesbarkeit aus.
Beachten Sie, dass Sie selten etwas so Ausführliches wie fill(oneunit(T), sz)
schreiben müssen, da normalerweise ein Literal oder etwas ähnlich Kompaktes anstelle von oneunit(T)
ausreicht. Mit zukünftigen Änderungen an Array-Konstruktoren können auch kürzere Beschwörungen möglich werden. Außerdem, sobald Sie zu fill
, werden Sie es lieb gewonnen, das versichere ich Ihnen :). Am besten!
Wir könnten einfach auswählen, ob ones
one
oder oneunit
. ones
und zeros
sollten als Komfortfunktionen betrachtet werden, was in Ordnung ist, solange sie eine klare Bedeutung in Bezug auf allgemeinere Funktionen haben.
Plan pro Triage: Kommen Sie nächste Woche erneut vorbei und ziehen Sie insbesondere in Betracht, ones
und zeros
in die MATLAB-Kompatibilitätsschicht zu verschieben, zugunsten von fill
in der Basis. Am besten!
ones
und oneunits
würden die beiden Optionen unterscheiden.
Ich bin mir nicht sicher, was ich davon halten soll, dies und zeros
auf MATLAB-Kompatibilität zu verschieben. Nur weil Mathworks es erfunden hat, heißt das nicht, dass es keine gute Komfort-API ist, auf die wir stolz sein können . Nur kurz - was waren die Gründe für diesen Gedanken? (Entschuldigung, ich muss sagen, dass die Triage viel effizienter erscheint, aber deutlich weniger transparent, wenn die Begründung nicht angegeben wird).
Es ist auch keine Änderung, die mich begeistert, aber ich habe sie angesprochen, weil es eine logische Inkonsistenz ist. Ich denke, es ist fair zu sagen, dass ones(T, sz)
fill(one(T), sz)
ones(T, sz)
impliziert, aber wirklich, was es unter der Haube tut, ist fill(oneunit(T), sz)
.
Eine Möglichkeit wäre, es in oneunits
umzubenennen. Eine Alternative wäre ein schrecklicher Tausch: one
-> algebraic_one
und oneunit
-> one
. Das ist kaputt und nicht durch Abwertung möglich, weshalb ich "schrecklich" sage.
Ja, ich würde sagen, dass das Hinzufügen von oneunits
und das Ändern von ones
in fill(one(T), ...)
die offensichtliche Lösung dafür wäre, oder?
Damit wäre ich gut. Aus Neugier, was sind die Verwendungen von fill(one(T), sz)
? Brauchen wir überhaupt ones
?
Haha :) Ich wollte fragen - wofür benutzt du fill(oneunits(T), sz)
? (ZB wofür würde ein Array mit 1 Meter oder 1 Kilogramm oder 1 Sekunde verwendet werden?)
Ich denke, fill(one(T), sz)
wird aus dem gleichen Grund wie zeros(T, sz)
, nämlich um ein Array für eine benutzerdefinierte Reduktionsoperation zu initialisieren. Ein Array z = zeros(T, sz)
ist bereit, seine Elemente mit z[i] += x
ergänzen, während o = fill(one(T), sz)
bereit ist, seine Elemente mit o[i] *= x
multiplizieren. Ich denke zum Beispiel an Situationen, in denen die Array-Elemente (relative) Wahrscheinlichkeiten darstellen könnten. In diesen beiden Fällen suche ich nach der Identität für den Operator +
bzw. *
(und keine additiven Generatoren).
Siehe auch #23544
Haha :) Ich wollte fragen - wofür verwendest du fill(oneunits(T), sz)? (ZB wofür würde ein Array mit 1 Meter oder 1 Kilogramm oder 1 Sekunde verwendet werden?)
Die abhängigen Variablen für eine ODE. Es wäre seltsam, wenn Sie nach einem Array vom Typ T
fragen würden.
Der grundlegende Grund, fill
zu bevorzugen, besteht darin, dass ein enger Satz leistungsstarker, orthogonaler, zusammensetzbarer Werkzeuge besser geeignet ist als eine größere Sammlung von Ad-hoc-, begrenzten und überlappenden Werkzeugen wie ones
/ zeros
/ trues
/ falses
. Die obige Diskussion hebt diesen Punkt hervor: Während fill
alle oben genannten Anwendungsfälle eindeutig und (in den meisten realen Anwendungen) prägnant abdeckt, sind die ones
/ zeros
/ trues
/ falses
Ansatz erfordert für jeden Anwendungsfall eine neue Funktion.
Ein paar relevante Beispiele aus Rewrites in base:
complex.(ones(size(Ac, 1)), ones(size(Ac, 1)))
wird
fill(1.0 + 1.0im, size(Ac, 1))
und
2ones(Int, 2, 2, 2)
wird
fill(2, (2, 2, 2)) # or fill(2, 2, 2, 2) if you prefer
Bitte beachten Sie, dass diese fill
Beschwörungsformeln einfacher, lesbarer, kompakter und effizienter sind als ihre nicht- fill
Gegenstücke, und base/
und test/
sind reich an Beispielen wie diesen. Wie bei jeder Veränderung erfordert der Übergang zu fill
eine anfängliche mentale Anpassung. Aber nach der Anpassung haben Sie mehr Kraft und Eleganz zur Hand :). Am besten!
@Sacha0 : trues
/ falses
sind nicht direkt durch fill
ersetzbar, sondern müssen fill!
mit einem BitArray
Initialisierer verwenden. Sie sind auch nicht in der Mehrdeutigkeit zwischen one
und oneunit
. Daher denke ich, dass sie überhaupt nicht in diese Diskussion passen.
Was ones
, bin ich im Allgemeinen sowieso dagegen, es abzulehnen, ich sehe den Vorteil nicht. Das Argument, dass einige Ausdrücke mit fill
effektiver geschrieben werden können, ist meiner Meinung nach nicht sehr überzeugend, da alle diese Beispiele ones
als Zwischenschritte verwenden, um etwas anderes zu tun; aber was ist, wenn Sie tatsächlich eine Reihe von Einsen wollen? Dann ist es länger, weniger offensichtlich und nur ärgerlicher, fill
. Der Vorschlag von @JeffBezanson gefällt base
oder test
mit fill
anstelle von ones
effektiver umgeschrieben werden können, steht dem momentan nichts im Wege.
@carlobaldassi Das stimmt zwar, aber eine schnelle GitHub-Suche zeigt, dass fast alle Verwendungen von ones
wirklich fill
, um Zwischenzuweisungen zu vermeiden ...
Die Verwendung von fill
für diese Fälle macht für mich Sinn. Beachten Sie, dass eine neue Methode fill(x, A) = fill!(x, copymutable(A))
die entsprechenden Methoden von ones
usw. ersetzen soll.
@TotalVerb Nach einem kurzen ones
, vielleicht sogar die Mehrheit (und selbst wenn sie nur 20% sagen würden, denke ich, dass mein Argument immer noch Bestand hat).
(Ich habe auch Vorbehalte gegen das Argument der Lesbarkeit, ich denke, es ist fragwürdig zu behaupten, dass zB fill(2, 2, 2, 2)
lesbarer ist als 2 * ones(Int, 2, 2, 2)
, oder dass, sagen wir, fill(k * 1.0, 3)
mehr wäre lesbar als k * ones(3)
; Ich bin überhaupt nicht überzeugt, dass es nur eine Frage der Gewohnheit ist. Dies ist jedoch ein kleiner Punkt.)
true/falses können nicht direkt durch Fill ersetzt werden, sondern müssen Fill verwenden! mit einem BitArray-Initialisierer. Sie sind auch nicht in der Mehrdeutigkeit zwischen one und oneunit enthalten. Daher denke ich, dass sie überhaupt nicht in diese Diskussion passen.
Tatsächlich sind trues
und falses
nicht direkt durch fill
ersetzbar :). Stattdessen sind trues
und falses
zusätzliche Instanzen des oben beschriebenen allgemeinen Problems / bezogen auf #11557 (und das die neuere Richtung von Array-Konstruktoren hoffentlich angehen wird). Andere Beispiele sind die Existenz von zB bones
, bzeros
, brand
, brandn
und beye
in BandedMatrices.jl und Äquivalente mit a d
Präfix in DistributedArrays.jl.
Was diejenigen angeht, bin ich im Allgemeinen sowieso dagegen, sie abzulehnen, ich sehe den Vorteil nicht. Das Argument, dass manche Ausdrücke mit Fill effektiver geschrieben werden können, ist meiner Meinung nach nicht sehr überzeugend, da alle diese Beispiele Einsen als Zwischenschritte verwenden, um etwas anderes zu tun
Nachdem ich gerade ein paar hundert Verwendungen von ones
in base umgeschrieben habe, kann ich die Aussage von
Eine schnelle GitHub-Suche zeigt, dass fast alle Verwendungen von Einsen wirklich Fill verwenden sollten, um Zwischenzuweisungen zu vermeiden ...
(Bearbeiten: Obwohl ich eher ungefähr die Hälfte als fast alle sagen würde, und geeignete Umschreibungen können etwas anderes sein als fill
.) Darüber hinaus hat mich diese Umschreiberfahrung gelehrt ...
aber was ist, wenn Sie tatsächlich eine Reihe von Einsen wollen? Dann ist die Verwendung von Fill länger, weniger offensichtlich und nur ärgerlicher.
... dass andererseits fill
dann häufig kürzer und einfacher ist: Die angefragten sind häufig nicht Float64
(stattdessen zB ones(Int, n...)
und ones(Complex{Float64}, n...)
), wobei fill
kürzer und einfacher ist, indem ein Literal zugelassen wird (zB fill(1, n...)
und fill(1.0 + 0im, n...)
). Messbar ausgedrückt ist der Branch, in dem ich ones
Aufrufe in base umgeschrieben habe, um die Zeichenanzahl ~5% kürzer von ones
-> fill
umschreibt. Am besten!
Um ein objektives Gefühl dafür zu bekommen, wie ones
in freier Wildbahn erscheint, habe ich alle ones
Aufrufe gesammelt, die auf den ersten zehn Seiten einer GitHub-Suche nach ones
in Julia-Code erscheinen, umgeschrieben jeder dieser Aufrufe nach Bedarf und klassifizierte die entsprechende Änderung (siehe
Die Analyse umfasste 156 ones
Calls. Von diesen Anrufen,
84 Anrufe (~54 %) waren Ad-hoc- fill
s. (Zum Beispiel vereinfacht sich ones(100)/sqrt(100)*7
zu fill(7/sqrt(100), 100)
oder besser noch zu fill(.7, 100)
. Mein Favorit war kron(0.997, ones(1, J*J*s)
-> fill(0.997, 1, J*J*s)
.)
3 Anrufe (~2%) waren Ad-hoc-Broadcasts. (Zum Beispiel vereinfacht sich A - ones(n,n)
zu A .- 1.
.)
5 Aufrufe (~3%) waren Ad-hoc-Vektorliterale. (Zum Beispiel vereinfacht sich ones(1)
zu [1.]
.)
1 Aufruf (~0,5 %) war semantisch eine Junk-Matrix-Konstruktion. (Obwohl dieses Muster in der Wildnis relativ ungewöhnlich ist, ist dieses Muster in test/
ziemlich häufig, da wir keinen prägnanten Komfortkonstruktor für nicht initialisierte Array
s haben, wie z. B. in <strong i="32">@test_throws</strong> DimensionMismatch BLAS.trsv(...,Vector{elty}(n+1))
Vergleich zu <strong i="34">@test_throws</strong> DimensionMismatch BLAS.trsv(...,ones(elty,n+1))
.)
Die verbleibenden Aufrufe waren semantisch sinnvoll als ones
, obwohl ones
oft nur deshalb Verwendung findet, weil es kurz ist, und nicht, weil one
spezifisch notwendig sind. Von diesen verbleibenden Anrufen,
13 Anrufe (~8%) waren etwas kürzer als fill
. (Zum Beispiel ones(Int, n, n)
-> fill(1, n, n)
oder ones(Float64, n)
-> fill(1., n)
.)
50 Anrufe (~32 %) waren etwas länger als fill
. (Zum Beispiel ones(n, n)
-> fill(1., n, n)
.)
Insgesamt sind ~60% der ones
Calls irgendwie besser geschrieben, ~8% sind semantisch halbwegs ones
und etwas kürzer als fill
und ~32% sind es halbwegs semantisch ones
und etwas länger als fill
.
Eine zusätzliche Beobachtung:
Ich bin nur auf eine Instanz eines ones
Aufrufs gestoßen, der ein Array-Argument akzeptierte, und es war nicht klar, ob das einschließende Snippet echter Code war. Die ones
Methoden, die ein Array-Argument akzeptieren, sehen also wenig oder gar keine Verwendung in freier Wildbahn.
Wirklich interessante Diskussion ... ging von der Gegenseite zur Für-Seite ... Auch als weiterer Sprachpräzedenzfall verwendet R rep
und matrix
in einer Weise, die dem fill
(entspricht nur den 1d und 2d Fällen) und man gewöhnt sich sehr schnell daran -- obwohl ich aus einer Nullen/Einsen Welt stamme.
Wow, danke @Sacha0 für diese Mühe!
Die Frage stellt sich natürlich als "Was ist mit zeros
"? Ich vermute, es wird deutlich mehr Nutzung geben und ein paar weitere Nutzungskategorien (einschließlich Dinge wie "Ich vertraue einfach nicht nicht initialisierten Arrays" oder "Ich weiß nicht, wie man die Array
Konstruktoren verwendet ").
Aus welchem Grund auch immer (ich schätze, es ist die Symmetrie von one
und zero
), bin ich etwas davon angezogen, entweder ones
und zeros
durch fill
zu ersetzen.
Die Sache mit Nullen ist, dass Sie sich in einer dieser Situationen befinden:
0I
.Es gibt wirklich keinen Anwendungsfall, bei dem die Zuweisung einer Matrix mit dichten Nullen tatsächlich eine gute Idee ist.
Das gilt vielleicht für die lineare Algebra. Es ist nicht ungewöhnlich, dass ich bei meiner Arbeit an Compilern und anderen Datenstrukturen eine nullinitialisierte Sammlung benötige. Vielleicht sind sie oft spärlich, aber es lohnt sich nicht, sie kompakt darzustellen.
Fair genug – manchmal ist dir die Dichte egal und die Einfachheit lohnt sich.
Triage: Es wurde beschlossen, dass wir nur die völlig nicht generischen Methoden beibehalten, dh zeros(dims...)
und ones(dims...)
und vielleicht auch zeros(dims)
und ones(dims)
.
@StefanKarpinski für Nutzungsempfehlungen bedeutet das, dass wir zeros(3, 3)
über fill(0.0, 3, 3)
für normalen Code empfehlen würden (wenn ein dichtes Array gewünscht wird usw.)? Einige Details zur Effizienz usw. liegen außerhalb meiner Reichweite, ich denke nur darüber nach, wie ich die besten / idiomatischen Praktiken in Julia weitergeben würde.
Diese Entscheidung erscheint mir sehr überraschend, es ist in der Basis nicht so üblich, Generika gezielt zu verhindern. Was ist die Überlegung dahinter? kommt diese Funktion von Matlab, wo sie nicht generisch ist (und nur mit Floats funktioniert)?
Triage: Es wurde beschlossen, dass wir nur die vollständig nicht generischen Methoden beibehalten
Was ist die Überlegung dahinter?
Hängt dieses Problem darüber hinaus irgendwie mit dem allgemeinen Problem der Konstruktion von Arrays zusammen, das derzeit in Betracht gezogen wird, und wenn ja, wie hilft dies? Oder versuchen wir, die Anzahl der exportierten Methoden von Base
zu reduzieren? (Oder etwas ganz anderes?)
Bericht folgt in https://github.com/JuliaLang/julia/issues/24595 :). Am besten!
ones
, zeros
und fill
. Ersteres zu lesen ist wertvoll, um Letzteres zu schätzen. Am besten!Nun, zumindest den Fall Float64
retten ist besser als nichts.
Ich glaube, dass der Fall von ganzzahligen Nullen auch ziemlich relevant ist, was - ich nehme an - im Grunde der Punkt ist, den @vtjnash hier gemacht hat .
Es sollte auch beachtet werden, dass zeros
nicht das "Problem" hat, das 3 * ones(n)
Anti-Pattern zuzulassen. Tatsächlich verstehe ich nicht wirklich, warum ones
und zeros
sollten, außer im weitesten Sinne als Convenience-Konstruktoren. Es gibt keine wirkliche "Symmetrie" zwischen diesen beiden.
Ein paar zusätzliche Anmerkungen zur statistischen Analyse , da sie die Grundlage der folgenden Diskussionen und der Zuschreibung von #24595 zu sein scheint. Erstens reichen zehn Seiten nicht wirklich aus, um grobe Rückschlüsse auf das Geschehen in der Wildnis zu ziehen, sie können bestenfalls einen groben Eindruck vermitteln. Einige Dateien kamen zum Beispiel direkt von Matlab, wie aus ihrem Namen/Stil hervorgeht. Zweitens, wie ich vermutet habe, zeigt selbst diese Analyse, dass ungefähr die Hälfte der Verwendungen von ones
darin "legitim" waren. Drittens sagt der Blick auf Code wie diesen nichts darüber aus, wenn 3 * ones(...)
wirklich ein Anti-Muster ist, das Leistungsprobleme verursacht, oder es ist ein Stück Code, das überhaupt keine Auswirkungen auf die Leistung hat (und der Autor kann entschieden haben, dass es so einfach lesbarer geschrieben ist - was meiner Meinung nach niemanden etwas angeht, in diesem Fall anders zu entscheiden).
In Bezug auf den letzten Punkt, und ich denke, was noch wichtiger ist, wird das, was Sie von einer Github-Suche sehen, nie berücksichtigen, was in der REPL von Leuten vor sich geht, die in Julia Erkundungs- / Vorarbeiten machen. Genau hier kommen Komfortfunktionen am praktischsten zum Einsatz, und das Wegnehmen ohne erkennbaren Grund ist entsprechend am nervigsten. Mein Punkt ist, dass es ein großes Ziel ist, einen konsistenten Satz orthogonaler Primitive zu haben, die es ermöglichen, generischen und effizienten Code zu schreiben, und der Aufwand, um dorthin zu gelangen, ist wirklich lobenswert; es ist nur so, dass nicht jeder Code schöner, generischer und zusammensetzbarer Bibliothekscode sein soll. Nur meine zwei Cent.
Bezüglich
Ich glaube, dass der Fall von ganzzahligen Nullen auch ziemlich relevant ist, was - ich nehme an - im Grunde der Punkt ist, den
@vtjnash
hier gemacht hat.
was bezieht sich auf
Es ist nicht ungewöhnlich, dass ich bei meiner Arbeit an Compilern und anderen Datenstrukturen eine nullinitialisierte Sammlung benötige. Vielleicht sind sie oft spärlich, aber es lohnt sich nicht, sie kompakt darzustellen.
Beachten Sie, dass fill
in diesem Fall genauso gut oder besser ist: fill(0, shape...)
versus zeros(Int, shape...)
.
Was deine anderen Punkte angeht, könnte #24595 lesenswert sein :). Am besten!
Ich finde einfach, dass zeros(Int, 10, 10)
lesbarer/expliziter ist als fill(0, 10, 10)
und zeros(T, k)
besser ist als fill(zero(T), k)
. Warum können wir nicht einfach beides haben? Ich halte das Argument nicht, dass zeros
unter dem gleichen Mehrdeutigkeitsproblem leidet wie ones
.
Was deine anderen Punkte angeht, könnte #24595 lesenswert sein
Ich hatte es gelesen. (Ich habe es sogar verlinkt.)
Ich hatte es gelesen. (Ich habe es sogar verlinkt.)
Nachdem Sie #24595 vollständig gelesen und sorgfältig geprüft haben, wissen Sie, dass #24595: (1) ein viel umfassenderes Thema betrifft, von dem Convenience-Konstruktoren nur ein Teil sind; und (2) berücksichtigt weit mehr als nur die oben gepostete statistische Analyse und die Punkte, auf die Sie sich hier konzentrieren.
Ich weiß zu schätzen, dass Sie ones
und zeros
; dein Gefühl ist laut und deutlich durchgekommen :). Daher sind die Chancen gut, dass unsere Bandbreite besser genutzt wird, um andere Fronten voranzutreiben, als dieses Gespräch in seiner jetzigen Form fortzusetzen. Am besten!
Gibt es einen generischen fill
Eingang zusammen mit der zeros
Änderung? zeros
hatte eine sehr legitime Verwendung für generische Programmierung, da es viel sicherer ist als similar
, also alle von DiffEq, und dann weiß ich, dass Optim und NLsolve kürzlich von der Allokation mit similar
zu zeros
da zu wissen, dass alles mit Nullen belegt ist, stoppt viele Fehler. Jetzt scheint es jedoch keine Methode zu geben für:
zeros(X)
mehr, außer:
similar(X); fill!(X,0)
weil das aktuelle fill
nur Array
s aufbaut und somit keine Typübereinstimmung wie similar
oder zeros
macht. Ich weiß, dass einige Leute zeros
missbraucht haben, um zuzuordnen, wenn sie es nicht hätten tun sollen, aber die Zuordnung mit zeros
ist in vielen Fällen sehr vernünftig. Ich hoffe, dass eine Abkürzung von fill(0,X)
hinzugefügt wird, um diese Lücke zu füllen.
Vielen Dank für den nachdenklichen Beitrag Chris! :) Hilft zero(X)
als vorläufiger Kurzschriftersatz?
Beachten Sie, dass solche Anwendungsfälle genau dort liegen, wo die Mehrdeutigkeit in zeros
und ones
problematisch sein kann: Liefert zeros(X)
ein Objekt mit eltype(X)
und gefüllt mit eltype(X)
additive Identität von fill!(similar(X), 0)
) oder stattdessen ein Objekt, das mit einer multiplikativen Null für eltype(X)
gefüllt ist (möglicherweise nicht von eltype(X)
)? (Zur Erweiterung siehe #24595.)
Das Konzept von fill(0, X)
in #11557 ein wenig diskutiert, und ich stimme zu, dass dies eine nützliche Verallgemeinerung von fill
. Danke und Bestes!
Das andere Problem ist, dass Arrays mit unkonventionellen Indizes mit etwas wie zeros(inds...)
erstellt werden möchten (da der Indextyp den Arraytyp bestimmt ). Aber für einen 1-d-Fall ist X
"das Array, dem Sie ähnlich sein möchten" oder "die Indizes für das gewünschte Array"? (Immerhin AbstractUnitRange <: AbstractArray
.) Bedeutet zeros(3:5)
konkret fill!(similar(3:5), 0)
oder fill!(OffsetArray(Vector{Float64}(3), 3:5), 0)
?
Verlinkung von https://github.com/JuliaLang/julia/pull/24656 , die zusätzliche Diskussionen über {ones|zeros }(A::AbstractArray, ...)
Convenience-Konstruktoren enthält. Am besten!
Ich bin überrascht, dass es als seltsam angesehen wird, zeros
zu verwenden, um Cache-Variablen gemäß #24656 zu erstellen. Ich würde denken, dass, wenn zeros
auf fast null reduziert würde, fast jeder Fall, in dem Leute similar
verwenden, stattdessen zeros
da dies dazu neigt, einige zu beheben Fehler. Ich denke, wir sollten mehr Leute dazu ermutigen, dies zu tun, da similar
ziemlich unsicher sein kann und keine Funktion zu haben und stattdessen fill!
+ similar
macht es weniger offensichtlich, dass das ist was die Leute tun sollen. Hier ist ein Kommentar zu diesem Popup in Optim.jl:
https://github.com/JuliaNLSolvers/NLsolve.jl/issues/89#issuecomment -294585960
Ich stimme @timholy jedoch zu, dass es nicht offensichtlich ist, wie es interpretiert werden sollte. Lassen Sie mich Sie auf ein wirklich nicht einfaches Beispiel in DiffEq hinweisen.
https://github.com/JuliaDiffEq/MultiScaleArrays.jl
MultiScaleArrays.jl hat abstrakte Arrays erstellt, bei denen es sich um rekursive graphähnliche Strukturen handelt, die in den diffeq-Lösern verwendet werden können (und ich denke, es ist jetzt möglicherweise mit Optim.jl und NLsolve.jl kompatibel?). Es ist unter anderem für biologische Modelle eine nette Annehmlichkeit. Wenn wir es in den ODE-Solver werfen, stellt sich die Frage: Was sollen wir die Cache-Arrays machen? In einigen Fällen ist es wichtig, dass der Benutzer das gewünschte Array zurückbekommt, da es in seiner ODE-Funktion f(t,u,du)
angezeigt wird und er es entsprechend behandeln möchte. In anderen Fällen wird es jedoch nur als etwas angezeigt, gegen das intern gesendet wird. Es gibt also zwei verschiedene Arten von Cache-Variablen.
Um dies zu handhaben, werfen Sie einen Blick auf den Cache für einen der Algorithmen:
https://github.com/JuliaDiffEq/OrdinaryDiffEq.jl/blob/master/src/caches/low_order_rk_caches.jl#L224 -L234
Hier ist rate_prototype = similar(u,first(u)/t,indices(u)
ähnlich, aber mit möglicherweise unterschiedlichem Eltype für Einheiten. Beachten Sie jedoch, dass es hier zwei verschiedene Typen gibt: similar(u)
vs similar(u,indices(u))
. Ich habe diese so interpretiert, dass sie "entsprechen dem Typ und der Form" vs "entsprechen der Form und dem Eltype, müssen aber nicht der gleiche Typ sein". Für ein AbstractMultiScaleArray
das erste ein weiteres AbstractMultiScaleArray
während das andere aus Geschwindigkeitsgründen, da es vom Benutzer nicht gesehen wird, nur ein Array
des entsprechenden erstellt Größe. Diese werden dann auf similar(u,T)
und similar(u,T,indices(u))
.
Vielleicht ist das nur ein Wortspiel von dem, was bereits existiert, aber ich denke, dies ist ein wichtiger Unterschied. Bei der generischen Programmierung haben Sie zwei separate Caches: benutzerorientierte Caches, die Typen abgleichen möchten, um ihre Erwartungen zu erfüllen, und interne Caches, die nur vom Algorithmus verwendet werden und so viel Geschwindigkeit wie möglich benötigen.
Beachten Sie, dass diese similar
weil ich zu faul war, eine zeros
Version davon zu entwickeln. Ich habe tatsächlich einen separaten Ort, an dem einige dieser Arrays auf Null gesetzt werden können, denn wenn der Benutzer nur du
in seiner f(t,u,du)
Ableitungsberechnung festlegt, neigen sie dazu, implizit zu denken, "was ich nicht setze, bedeutet". zero", was nur wahr ist, wenn es mit zeros
zugewiesen wurde, also versuche ich, so viel wie möglich mit zeros
vorzubelegen (das gleiche Problem tritt in NLsolve.jl auf) .
Hoffentlich war diese Erklärung nicht zu verwirrend. In all meinen Fällen kann ich einfach zu similar
wechseln, gefolgt von fill!
, aber ich weiß, dass einige Pakete dies nicht tun und dies eine Quelle für Fehler sein wird.
Interessant. Sie haben Recht, dass similar(A, inds)
möglicherweise einen anderen Typ als similar(A)
, aber im Allgemeinen habe ich immer daran gedacht, dass wahrscheinlich derselbe Typ erstellt wird, jedoch mit unterschiedlichen Indizes. Wenn Sie beispielsweise einen 1-dimensionalen Cache für eine spaltenweise Operation für ein 2-D-Objekt benötigen, würde ich similar(A, first(inds))
. (Natürlich ist es ein anderer Typ, da die Dimensionalität ein Typparameter ist, aber es könnte derselbe abstrakte Containertyp sein.) Sie können ihn auch verwenden, um einen 5x5-Cache einer kleinen Kachel usw. zu erstellen.
Insgesamt scheint dies ein herausforderndes Problem zu sein. Es ist ein bisschen spät im Spiel, aber sollten wir same
einführen? Es könnte die gleichen Argumente wie similar
, aber der Vertrag sieht vor, dass derselbe abstrakte Container zurückgegeben werden muss.
Ich könnte eine einargumentigen Form von Support same
, aber selbst das ist schwierig - beachten Sie, dass same(a)
nicht den gleichen Array - Typen zurückgeben könnte , wenn a
nicht unterstützt setindex!
weil same
und similar
nur nützlich sind, wenn Sie danach in das Array schreiben. Wir könnten dies zu einem Fehler für unveränderliches a
, aber als Schnittstelle für AbstractArray
scheint dies unnötig (und möglicherweise nicht hilfreich) zu sein, um korrekten, generischen Code zu erstellen.
Ebenso können wir nicht davon ausgehen, dass jedes AbstractArray
verschiedene Eltypes oder Indizes unterstützt. Für mich würde eine Form von same
mit zwei oder drei Argumenten nur an vielen Stellen Laufzeitfehler einführen und den Leuten gleichzeitig ein falsches Gefühl der Sicherheit geben, dass ihr allgemeiner Code für jeden AbstractArray
gut funktioniert.
Aber ist X für einen 1-d-Fall "das Array, dem Sie ähnlich sein möchten" oder "die Indizes für das gewünschte Array"?
Dies ist ein weiterer Grund, warum ich dafür bin, dass keys
einen Container mit identischen Indizes und Werten zurückgibt und dies dann zu einer Anforderung von similar
(es sei denn, Sie geben eine (einige) Ganzzahl(en) in . an wobei Base.OneTo
(CartesianRange) angenommen wird).
Diese Diskussion schwenkt in Richtung #18161 und sollte vielleicht dort fortgesetzt werden :).
Die letzte Triage tendierte dazu, nur ones
behalten.
Tut es weh, sie zu behalten? Ich denke, es hilft Menschen, die aus dem Nichts kommen, sich in Julia zu Hause zu fühlen.
Geschlossen, da wir ones
und zeros
behalten.
Hilfreichster Kommentar
Für mich sieht
fill(oneunit(T), sz)
im Vergleich zuones(T, sz)
wie ein nicht trivialer Verlust an Lesbarkeit aus.