Julia: "Einser" ablehnen?

Erstellt am 2. Nov. 2017  ·  46Kommentare  ·  Quelle: JuliaLang/julia

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 ?

decision linear algebra stdlib

Hilfreichster Kommentar

Für mich sieht fill(oneunit(T), sz) im Vergleich zu ones(T, sz) wie ein nicht trivialer Verlust an Lesbarkeit aus.

Alle 46 Kommentare

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:

  1. Sie müssen die meisten Nullen überschreiben – in diesem Fall ist es besser, ein Verständnis zu verwenden;
  2. Sie müssen die meisten Nullen nicht ersetzen – in diesem Fall ist es besser, eine Matrix mit geringer Dichte zu verwenden;
  3. Sie brauchen tatsächlich eine Matrix, die nur aus Nullen besteht – in diesem Fall verwenden Sie besser 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!

Das OP von 24595 hat den breiteren Kontext dieses Problems detailliert beschrieben, und jetzt befasst sich ein Follow-up in #24595 speziell mit den Convenience-Konstruktoren 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.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen