Julia: Globale Regeln für den Gültigkeitsbereich von Variablen führen zu unintuitiven Verhalten am REPL/Notebook

Erstellt am 21. Aug. 2018  ·  98Kommentare  ·  Quelle: JuliaLang/julia

Beispiel 1

Dies kam zu einem Schüler, der direkt von 0.6 auf 1.0 aktualisierte, also nie eine Chance hatte, eine Verfallswarnung zu sehen, geschweige denn eine Erklärung für neues Verhalten zu finden:

julia> beforefor = true
true

julia> for i in 1:2
         beforefor = false
       end

julia> beforefor  # this is surprising bit
true

julia> beforeif = true
true

julia> if 1 == 1
         beforeif = false
       end
false

julia> beforeif  # Another surprise!
false

julia> function foo()
         infunc = true
         for i in 1:10
           infunc = false
         end
         <strong i="7">@show</strong> infunc
       end
foo (generic function with 1 method)

julia> foo()  # "I don't get this"
infunc = false 

Beispiel 2

julia> total_lines = 0
0

julia> list_of_files = ["a", "b", "c"]
3-element Array{String,1}:
 "a"
 "b"
 "c"

julia> for file in list_of_files
         # fake read file
         lines_in_file = 5
         total_lines += lines_in_file
       end
ERROR: UndefVarError: total_lines not defined
Stacktrace:
 [1] top-level scope at ./REPL[3]:4 [inlined]
 [2] top-level scope at ./none:0

julia> total_lines  # This crushs the students willingness to learn
0

Ich "verstehe", warum dies geschieht, in dem Sinne, dass ich es mit ausreichendem Verweis auf die Arkana im Handbuch darüber erklären kann, was Scopes einführt und was nicht, aber ich denke, dass dies für die interaktive Verwendung problematisch ist.

Im ersten Beispiel erhalten Sie einen stillen Fehler. In Beispiel zwei erhalten Sie eine Fehlermeldung, die sehr "es-kein-Löffel" lautet. Das ist ungefähr vergleichbar mit etwas Python-Code, den ich heute bei der Arbeit in ein Notizbuch geschrieben habe.

Ich bin mir nicht sicher, was die Regeln in Python sind, aber ich weiß, dass Sie im Allgemeinen keine Dinge im globalen Bereich zuweisen können, ohne global aufzurufen. Aber beim REPL funktioniert es, vermutlich weil beim REPL die Regeln anders sind oder die gleiche Logik angewendet wird, als ob sie alle im Funktionsumfang wären.

Ich kann die Regeln nicht genug sprachlich machen, um die konkrete Änderung vorzuschlagen, die ich gerne hätte, und basierend auf Slack wird dies von einigen Leuten nicht einmal unbedingt als ein Problem wahrgenommen, also weiß ich nicht, wohin ich damit gehen soll, außer zu markieren Sie es.

Querverweise:

19324

https://discourse.julialang.org/t/repl-and-for-loops-scope-behavior-change/13514
https://stackoverflow.com/questions/51930537/scope-of-variables-in-julia

REPL minor change

Hilfreichster Kommentar

@JeffBezanson , denken keine Programmierkurse und die Studenten haben oft keinen Programmierhintergrund. Wir machen nie strukturierte Programmierung – fast alles ist interaktiv mit kurzen Ausschnitten und globalen Variablen.

Darüber hinaus verwende ich eine dynamische Sprache in erster Linie, um fließend zwischen interaktiver Erkundung und disziplinierterer Programmierung zu wechseln. Die Unfähigkeit, denselben Code in einem globalen und einem Funktionskontext zu verwenden, ist ein Hindernis, selbst für jemanden, der es gewohnt ist, Konzepte zu definieren, und es ist viel schlimmer für Studenten ohne CS-Hintergrund.

Alle 98 Kommentare

(Per @mlubin ist dies die relevante Änderung https://github.com/JuliaLang/julia/pull/19324)

Stefan schlug hier vor, dass eine Möglichkeit zur Lösung dieses Problems das automatische Umschließen von REPL-Einträgen in let Blöcke ist

Aber wäre das nicht verwirrend, da du es nicht tun könntest?

a = 1

und danach a ? Es sei denn, global wird für alle Aufgaben der obersten Ebene eingefügt, denke ich?

Das Verhalten würde nicht darin bestehen, alles in einen let Block zu packen – es ist komplizierter. Sie müssen jedes Global, das innerhalb des Ausdrucks zugewiesen ist, let-binden und dann den let-gebundenen Wert am Ende des Ausdrucks in ein Global extrahieren.

Sie würden also aus a = 1 etwas wie a = let a; a = 1; end . Und sowas wie

for i in 1:2
    before = false
end

würde so aussehen:

before = let before = before
    for i in 1:2
        before = false
    end
end

Ehrlich gesagt bin ich ziemlich verärgert, dass die Leute dieses Feedback erst jetzt geben. Diese Änderung ist seit zehn Monaten auf Master.

Ich bin schuldig, dass ich dem Master bis vor kurzem nicht sehr geschlossen gefolgt bin, daher kommt dieses Feedback in der Tat etwas spät. Mehr als ein Problem für Programmierer (die meisten for Schleifen befinden sich in einer Funktion im Bibliothekscode) Ich fürchte, dies ist ein Problem für den Unterricht. Oft werden for Schleifen vor Funktionen oder Scopes gelehrt (natürlich müssen Sie Scopes verstehen, um wirklich zu verstehen, was vor sich geht, aber beim Lehren werden die Dinge oft vereinfacht).

Hier wird es etwas schwierig, einem Anfänger das Summieren von Zahlen von 1 bis 10 beizubringen, ohne Funktionen oder globale Variablen zu erklären.

Ehrlich gesagt bin ich ziemlich verärgert, dass die Leute dieses Feedback erst jetzt geben. Diese Änderung ist seit zehn Monaten auf Master.

Um fair zu sein, wurde Julia 0.7 vor 13 Tagen veröffentlicht. Dies ist eine neue Änderung für die meisten Julia-Benutzer.

Ehrlich gesagt bin ich ziemlich verärgert, dass die Leute dieses Feedback erst jetzt geben. Diese Änderung ist seit zehn Monaten auf dem Master

Leider für diejenigen von uns, die nicht mit dem Leben am Rande umgehen können, ist es aus unserer Sicht brandneu.

Ehrlich gesagt bin ich ziemlich verärgert, dass die Leute dieses Feedback erst jetzt geben. Diese Änderung ist seit zehn Monaten auf Master.

Und für diejenigen von uns, die ermutigt wurden, sich von den Entwicklungszweigen fernzuhalten, "ist es aus unserer Sicht brandneu."

Können wir uns bitte jetzt wieder auf das vorliegende Problem konzentrieren, anstatt eine Metadiskussion darüber zu führen, wie lange die Leute dies testen mussten. Es ist, was es gerade ist, also schauen wir nach vorne.

Ich bin schuldig, dass ich dem Master bis vor kurzem nicht sehr geschlossen gefolgt bin, daher kommt dieses Feedback in der Tat etwas spät. Mehr als ein Problem für Programmierer (die meisten for-Schleifen befinden sich in einer Funktion im Bibliothekscode) Ich fürchte, dies ist ein Problem für den Unterricht. Oft werden for-Schleifen vor Funktionen oder Scopes gelehrt (natürlich müssen Sie Scopes verstehen, um wirklich zu verstehen, was vor sich geht, aber beim Lehren werden die Dinge oft vereinfacht).

Hier wird es etwas schwierig, einem Anfänger das Summieren von Zahlen von 1 bis 10 beizubringen, ohne Funktionen oder globale Variablen zu erklären.

Dies ist ein großer Punkt. Nachdem man herausgefunden hat, was das Problem wirklich ist, ist es überraschend, wie wenig es tatsächlich auftaucht. Es ist weniger ein Problem mit viel Julia-Code in freier Wildbahn und in Tests, und es hat viele Variablen aufgedeckt, die versehentlich global waren (in beiden Tests von Julia Base gemäß der ursprünglichen PR, und ich habe dies bei den meisten bemerkt .) DiffEq-Tests). In den meisten Fällen scheint es, dass das subtil falsche Verhalten nicht das ist, was Sie erhalten (eine Änderung in einer Schleife erwarten), sondern die Erwartung, eine Variable in einer Schleife verwenden zu können, ist meiner Meinung nach die überwiegende Mehrheit der Fälle wo dies beim Aktualisieren von Testskripten auf v1.0 auftaucht. Das Gute ist also, dass dem Benutzer in den meisten Fällen ein Fehler angezeigt wird, der nicht schwer zu beheben ist.

Das Schlimme ist, dass es ein wenig ausführlich ist, global x in die Schleifen einfügen zu müssen, und jetzt unterscheidet sich Ihr REPL-Code auch vom Funktionscode. Ob es intuitiveres Verhalten als zuvor ist oder nicht, ist eine schwierige Meinung, da es definitiv einige Grenzfälle im harten/weichen lokalen Scoping gab und dies daher eindeutig einfacher zu erklären ist. Gleichzeitig ist es zwar viel prägnanter als das Verhalten zuvor, aber es ist jetzt einfacher, die Randfälle zu erreichen, in denen es wichtig ist, die Scoping-Regeln zu verstehen. ️.

Ich für meinen Teil würde gerne die Experimente mit let Blockierung sehen. Dies würde den Aspekt "Sie wollten nicht wirklich so viele Globals" beibehalten, zusammen mit der vereinfachten Scoping-Erklärung, während gleichzeitig REPL-Code sich wie Funktionsinnenräume verhält (was anscheinend das ist, was wir immer wollten). Oder umgekehrt, die Leute dazu zu bringen, Variablen anzugeben, die sie als Globals fungieren möchten

global x = 5
for i = 1:5
  println(x+i)
end

könnte eine nette Möglichkeit sein, die Explizitheit beizubehalten, und würde den "REPL-Code ist aufgrund von Globals langsam" viel offensichtlicher machen. Der Nachteil ist, dass das Einwerfen von Dingen in eine Funktion nicht die global Marker erfordern würde.

Aber wenn man bedenkt, wie sich dies zeigt, ist es nicht wirklich bahnbrechend oder ein Showstopper. Ich würde es als Warze klassifizieren, die in jedem Workshop erwähnt werden sollte, aber es ist nicht so, dass v1.0 deswegen unbrauchbar ist. Ich hoffe, dass das Ändern dieses Verhaltens nicht als brechend eingestuft wird und v2.0 erfordert.

Ich bin mir nicht so sicher, ob mir die Idee gefällt, dass sich das REPL wie ein Funktionsinterieur verhalten sollte. Das ist es eindeutig nicht, daher erwarte ich, dass es sich wie ein globaler Bereich verhält. Für mich wäre die REPL, die sich nicht wie ein globaler Geltungsbereich verhält, möglicherweise noch verwirrender als die Diskrepanz, die dieses Problem verursacht.

Unabhängig davon denke ich, dass die Dokumentation zu diesem Thema zumindest etwas expliziter sein sollte. Wenn ich die Dokumente beiläufig lese, wäre ich davon ausgegangen, dass Sie das Schlüsselwort local , damit das Verhalten standardmäßig im globalen Bereich auftritt.

Ich für meinen Teil würde gerne die Experimente mit let Blockierung sehen. Dies würde den Aspekt "Sie wollten nicht wirklich so viele Globale" zusammen mit der vereinfachten Erklärung des Umfangs beibehalten, während sich gleichzeitig REPL-Code wie Funktionsinnenräume verhält (was anscheinend das ist, was wir immer wollten).

Wenn wir nach "REPL ist das gleiche wie das Innere einer Funktion" suchen, sollten wir auch an outer denken:

julia> i = 1
1

julia> for outer i = 1:10
       end
ERROR: syntax: no outer variable declaration exists for "for outer"

gegen:

julia> function f()
          i = 0
          for outer i = 1:10
          end
          return i
       end
f (generic function with 1 method)

julia> f()
10

Ehrlich gesagt bin ich ziemlich verärgert, dass die Leute dieses Feedback erst jetzt geben. Diese Änderung ist seit zehn Monaten auf Master.

Die Leute haben Master nicht für interaktiven Gebrauch oder für den Unterricht verwendet, sondern zum Upgrade von Paketen, die davon nur minimal betroffen sind und meist von erfahrenen Programmierern geschrieben werden.

(Ich war jedoch einer der wenigen Leute, die in Nr. 19324 Feedback gaben, wo ich für das alte Verhalten argumentierte.)

Ein sicherer Ausweg wäre, zum alten Verhalten zurückzukehren (idealerweise nicht durch Einfügen von impliziten let Blöcken oder ähnlichem – einfach den alten Code in julia-syntax.scm als Option wiederherstellen) in die REPL. Oder besser gesagt, um es in Umgebungen wie IJulia verfügbar zu machen, die es benötigen könnten, fügen Sie ein soft_global_scope=false Flag zu include , include_string und Core.eval , um die altes Verhalten.

(Ich war jedoch einer der wenigen Leute, die in Nr. 19324 Feedback gaben, wo ich für das alte Verhalten argumentierte.)

Ja, und ich weiß es sehr zu schätzen. Es spielt jetzt keine große Rolle, da wir die Wahl getroffen haben, es zehn Monate backen lassen und es jetzt mit einem langfristigen Engagement für Stabilität veröffentlicht haben. Das einzige, was Sie jetzt tun müssen, ist, sich darauf zu konzentrieren, was in Zukunft zu tun ist.

Die Möglichkeit, zwischen dem alten und dem neuen Verhalten zu wählen, ist interessant, aber es fühlt sich sehr hackig an. Das heißt, wir haben nicht nur manchmal ein Scoping-Verhalten, das anscheinend jeder unglaublich verwirrend fand, sondern wir haben es nicht immer und ob wir es haben oder nicht, hängt von einer globalen Flagge ab. Das fühlt sich ziemlich unbefriedigend an, fürchte ich.

Die Möglichkeit, zwischen dem alten und dem neuen Verhalten zu wählen, ist interessant, aber es fühlt sich sehr hackig an.

Wenn jemand eine Soft-Scope-AST-Transformation "unbreak me" implementiert, ist es sehr verlockend, sie in IJulia, OhMyREPL usw. zu verwenden.

Das ist nicht das, was ich sage. Natürlich sollten wir in all diesen Kontexten dieselbe Lösung verwenden. Die Implementierung als zwei verschiedene Variationen von Bereichsregeln scheint jedoch weniger sauber zu sein als die Implementierung als Codetransformation mit einem Satz von Bereichsregeln. Aber vielleicht sind diese funktional gleichwertig. Es scheint jedoch einfacher zu erklären in Bezug auf die neuen einfacheren Bereichsregeln + eine Transformation, die Eingaben im REPL-Stil aufnimmt und sie vor der Auswertung transformiert.

Dies könnte als Meta.globalize(m::Module, expr::Expr) , das einen Ausdruck transformiert, indem alle im Modul vorhandenen Globals automatisch als global annotiert werden, wenn sie innerhalb eines Nicht-Funktionsbereichs der obersten Ebene zugewiesen werden. Natürlich denke ich, dass das dem alten Parser entspricht, aber etwas transparenter, da Sie Meta.globalize selbst aufrufen und sehen können, was die REPL auswertet.

Dies könnte als Meta.globalize(m::Module, expr::Expr) , das einen Ausdruck transformiert, indem alle im Modul vorhandenen Globals automatisch als global annotiert werden, wenn sie innerhalb eines Nicht-Funktionsbereichs der obersten Ebene zugewiesen werden.

Ich habe tatsächlich vor ein paar Minuten damit begonnen, so etwas zu implementieren. Es sieht jedoch so aus, als wäre es viel einfacher als Option in julia-syntax.jl zu implementieren:

  • Das Schreiben einer externen AST-Transformation ist möglich, aber es scheint, als gäbe es viele knifflige Eckfälle – Sie müssen die Scoping-Regeln im Grunde neu implementieren – während wir den Code bereits in julia-syntax.scm hatten, um es richtig zu machen.
  • Noch komplizierter ist es für etwas wie IJulia, das derzeit include_string , um einen ganzen Codeblock auszuwerten und den Wert des letzten Ausdrucks zu erhalten. Wir müssten nicht nur zum Parsen von Ausdruck für Ausdruck wechseln, sondern es sind möglicherweise einige Hacker erforderlich, um die ursprünglichen Zeilennummern zu erhalten (für Fehlermeldungen usw.). (Obwohl ich für ChangePrecision.jl einen
  • Ganz zu schweigen von den Leuten, die include externe Dateien verwenden, die von Ihrer AST-Transformation nicht erfasst würden.

Es scheint jedoch einfacher zu erklären in Bezug auf die neuen einfacheren Bereichsregeln + eine Transformation, die Eingaben im REPL-Stil aufnimmt und sie vor der Auswertung transformiert.

Ich bezweifle ernsthaft, dass dies neuen Benutzern leichter zu erklären wäre, als nur zu sagen, dass die Regeln für die interaktive Verwendung oder für include mit einem bestimmten Flag weniger wählerisch sind.

Hier ist ein grober Entwurf einer globalize(::Module, ast) Implementierung: https://gist.github.com/stevengj/255cb778efcc72a84dbf97ecbbf221fe

Okay, ich habe herausgefunden, wie ich eine globalize_include_string Funktion implementieren kann, die Zeilennummerninformationen beibehält , und habe sie zu

Ein möglicher (nicht bahnbrechender) Weg nach vorne, wenn die Leute diesen Ansatz mögen:

  1. Geben Sie ein SoftGlobalScope.jl-Paket mit den Funktionen globalize usw. frei.
  2. Verwenden Sie SoftGlobalScope in IJulia (und möglicherweise Juno, vscode und OhMyREPL).
  3. Falten Sie die SoftGlobalScope-Funktionen in eine zukünftige Version des REPL-stdlib-Pakets und verwenden Sie es in der REPL.

Oder ist es praktisch, es sofort in REPL.jl zu rollen? Mir ist nicht ganz klar, wie stdlib-Updates in 1.0 funktionieren.

Bitte werfen Sie einen Blick auf meine Implementierung, falls ich etwas übersehe, das es zerbrechlich macht.

Können wir es nicht als nicht standardmäßiges Feature der REPL in 1.1 haben?

Duplikat von #28523 und #28750. Denjenigen, die sagen, dass sie die Leute nicht über globale Variablen unterrichten wollen, empfehle ich, zuerst Funktionen zu lehren, bevor for Schleifen durchlaufen werden. Funktionen sind ohnehin grundlegender, und dies wird dazu beitragen, die Erwartung zu erhöhen, dass Code in Funktionen geschrieben werden sollte. Obwohl ich die Unannehmlichkeiten verstehe, kann dieses Scoping-Verhalten in einen pädagogischen Vorteil umgewandelt werden: "Tatsächlich sind globale Variablen eine so schlechte Idee, insbesondere wenn sie in Schleifen verwendet werden, dass Sie sich bei der Sprache nach hinten beugen müssen, um sie zu verwenden."

Das Hinzufügen einer nicht standardmäßigen Funktion zur REPL dafür scheint mir jedoch in Ordnung zu sein.

@JeffBezanson , denken keine Programmierkurse und die Studenten haben oft keinen Programmierhintergrund. Wir machen nie strukturierte Programmierung – fast alles ist interaktiv mit kurzen Ausschnitten und globalen Variablen.

Darüber hinaus verwende ich eine dynamische Sprache in erster Linie, um fließend zwischen interaktiver Erkundung und disziplinierterer Programmierung zu wechseln. Die Unfähigkeit, denselben Code in einem globalen und einem Funktionskontext zu verwenden, ist ein Hindernis, selbst für jemanden, der es gewohnt ist, Konzepte zu definieren, und es ist viel schlimmer für Studenten ohne CS-Hintergrund.

Denken Sie daran, dass viele von uns Julia gerne als Ersatz für Matlab usw. in technischen Kursen wie Lineare Algebra und Statistik verwenden würden. Dies sind keine Programmierkurse und die Studenten haben oft keinen Programmierhintergrund. Wir machen nie strukturierte Programmierung – fast alles ist interaktiv mit kurzen Ausschnitten und globalen Variablen.

Viele von uns Julia-Benutzern haben absolut 0 CS-Hintergrund (einschließlich mir), aber es scheint mir, dass die richtige Einstellung ( insbesondere für Studenten) die Bereitschaft zum Lernen ist, anstatt zu fordern, dass die Dinge zum Schlechteren geändert werden, um unserer Naivität entgegenzukommen.

Jetzt bin ich nicht unbedingt was impliziert , dass diese besondere Veränderung zum Schlechteren sein würde , wie ich nur ein begrenztes Verständnis von dem , was hier vor sich geht, aber wenn es der Fall ist , dass dies eine erhebliche Komplikation ist oder macht es extrem einfach unnötig zu schreiben schlecht performanter Code scheint es nicht wert zu sein, eine Änderung vorzunehmen, um ein besseres Vorlesungsbeispiel zu haben. Sie können die Gesetze der Physik nicht ändern, damit die Elektrostatik-Beispiele, die Sie Erstsemestern zeigen, besser auf das wirkliche Leben anwendbar sind.

Meine Frage als Nicht-CS-Benutzer, der sich auch um die Leistung kümmert, lautet also, wie ich es wahrscheinlich vermasseln würde, wenn dies zum Standardverhalten würde. Ist es buchstäblich nur die Art von Beispielen, die wir hier sehen, die ein Problem darstellen (die mir bereits bekannt waren), oder werden wir dies wahrscheinlich oft auf subtilere Weise vermasseln?

Für das, was es wert ist, stimme ich zu, dass ein unterschiedliches Verhalten von Code je nach einschließendem Bereich eine im Allgemeinen unerwünschte Funktion ist.

Das interaktive Schreiben von Code zu erschweren, Anfänger zu zwingen, ihre ersten Schleifen zu schreiben, um obskure Bereichsregeln zu verstehen, und das Einfügen von Code aus Funktionen, die nicht in globalen Bereichen funktionieren, hilft Programmierern nicht dabei, schnellen Code in Funktionen zu schreiben. Es macht es nur schwieriger, Julia interaktiv zu verwenden und für Anfänger schwieriger.

Können wir es nicht als nicht standardmäßiges Feature der REPL in 1.1 haben?

Die Option "unbreak me" als Standard zu wählen, scheint klüger zu sein, insbesondere eine Option, die sich direkt an Anfänger richtet. Wenn es sich um eine nicht standardmäßige Option handelt, werden genau die Personen, die sie am dringendsten benötigen, diejenigen sein, die sie nicht aktiviert haben (und nicht wissen, dass sie existiert).

Was würde der vorgeschlagene REPL-Modus mit include ed-Skripten machen? Würde die Auswertung globaler Anweisungen davon abhängen, ob der REPL-Modus aktiviert ist? Wenn ja, würde dies IMO im Widerspruch zum Stabilitätsversprechen von 1.0 stehen.

Wenn wir so etwas tun, scheint es sinnvoll zu sein, dass das Modul bestimmt, wie es funktioniert. Main wäre also ein "Soft-Scope"-Modul, während andere Module standardmäßig "hard-Scope"-Module wären.

Ich war daran interessiert zu sehen, ob es möglich ist, die REPL zu patchen , um die globalize Funktion von @stevengj zu verwenden, und es scheint, dass dies ohne großen Aufwand möglich ist (wenn auch ziemlich hackig). Siehe das Wesentliche . Dies funktioniert nicht mit Juno (oder etwas anderem, das Core.eval direkt aufruft).

Ich werde dies nicht empfehlen, aber es ist für mich sehr nützlich, wenn ich schnelle und schmutzige Datenanalysen durchführe. Ich würde sehr gerne eine (besser durchdachte) Lösung sehen, da es für unerfahrene und oft widerstrebende Programmierer (dh meine Schüler) wirklich ziemlich verwirrend ist, wenn Sie keinen Code aus einer Funktion kopieren und in die REPL einfügen können, um ihn zu sehen was es macht und umgekehrt.

julia> a = 0                                                                
0                                                                           

julia> for i = 1:10                                                         
         a += i                                                             
       end                                                                  
ERROR: UndefVarError: a not defined                                         
Stacktrace:                                                                 
 [1] top-level scope at .\REPL[2]:2 [inlined]                               
 [2] top-level scope at .\none:0                                            

julia> using SoftGlobalScope                                                
[ Info: Precompiling SoftGlobalScope [363c7d7e-a618-11e8-01c4-4f22c151e122] 

julia> for i = 1:10                                                         
         a += i                                                             
       end                                                                  

julia> a                                                                    
55                                                                          

(Übrigens: Das obige ist ungefähr so ​​​​viel Tests, wie es hatte!)

Was würde der vorgeschlagene REPL-Modus mit enthaltenen Skripten tun?

Nichts. Grundsätzlich wird vorgeschlagen, dass dies nur für Code gilt, der an einer interaktiven Eingabeaufforderung eingegeben wird. Sobald Sie damit beginnen, Dinge in Dateien zu speichern, müssen Sie die Regeln für den "harten Bereich" lernen. Wenn Sie anfangen, Code in Dateien einzufügen, sollten Sie hoffentlich damit beginnen, Funktionen zu verwenden.

Es ist nicht ideal, wenn es wählerischere Bereichsregeln für globalen Code in Dateien gibt als bei der Eingabeaufforderung. Aber ich denke, dass #19324 in Kombination mit dem Stabilitätsversprechen von Julia 1.0 uns keine idealen Optionen lässt.

@stevengj :

@JeffBezanson , denken

Nachdem ich Kurse mit Julia für Studenten unterrichtet habe, die zuvor Matlab/R/... Gleichzeitig halte ich es jedoch nicht für sinnvoll, Julia nur als Matlab-Ersatz usw. möglicherweise mit noch größeren Kosten verbunden, als zu investieren, um zu verstehen, wie sich Julia von diesen anderen Sprachen unterscheidet (siehe Beiträge mit den Themen "Ich habe diesen Code von Matlab übersetzt und er ist 10x langsamer").

Ich denke, das Hauptproblem ist das stille Versagen; das Problem an sich ist leicht zu verstehen und zu beheben. Ich würde vorschlagen, das neue Verhalten beizubehalten, aber eine Warnung in Main (standardmäßig; es sollte möglich sein, es zu deaktivieren).

Für mich ist das größere Problem die wahrgenommene Inkonsistenz. Das heißt, ich bin damit einverstanden, dass Julia Dinge anders macht, aber:

  • Warum sollte aus einer Funktion eingefügter Code in einer REPL nicht funktionieren?
  • Keine andere Sprache, die ich jemals verwendet habe, hat dieses Verhalten, und es ist ein weiteres Hindernis für die Annahme
  • Warum verhalten sich for Blöcke anders als begin und if Blöcke? ( if Ich verstehe das irgendwie, aber ein Block ist [sollte] ein Block.).

Was Aufzählungspunkt 2 betrifft, denke ich, dass dies eine größere Sache ist, als wir, die wir Julia seit einiger Zeit verwenden (und der Sprache verpflichtet sind), möglicherweise verstehen. Ich kann Ihnen sagen, dass ich derzeit 0 für 7 bin, um meine Gruppe davon zu überzeugen, Code in Julia zu schreiben; zwei davon waren auf dieses for Schleifenproblem zurückzuführen, das ich nicht erklären konnte, da ich damit noch nie zuvor konfrontiert war. Den Rest können wir wohl meinem Mangel an Charisma verdanken.

Ich würde es vorziehen, sicherzustellen, dass sich Code, der aus einer Funktion in eine REPL eingefügt wird, genauso verhält wie die Funktion, und dass for Schleifen das Erwartete tun, wenn sie zur interaktiven Datenanalyse verwendet werden; das heißt, dass sie externe / globale Variablen mutieren, wenn sie ohne spezielle Schlüsselwörter angewiesen werden.

Ich glaube nicht, dass die Verwendung von Julia nur als Matlab-Ersatz usw. ein gangbarer Ansatz ist: Wie unzählige Male durch Fragen zu Discourse und StackOverflow gezeigt wurde, kann dies zu Leistungsfallen führen, die schwer zu beheben und zu verstehen sind und möglicherweise noch höhere Kosten nach sich ziehen als in das Verständnis zu investieren, wie sich Julia von diesen anderen Sprachen unterscheidet.

Sorry, aber dieses Argument finde ich lächerlich. Ich rede nicht von Klassen, in denen ich Programmieren unterrichte. Es gibt einen Platz für einfache interaktive Berechnungen, und in Nicht-CS-Klassen ist es üblich, Programmiersprachen zunächst als "verherrlichter Taschenrechner" kennenzulernen. Das Unterrichten von Performance Computing in Julia ist ein ganz anderer Prozess – aber es schadet nicht, wenn sie Julia bereits als "Rechner" verwendet haben.

Wenn Sie damit beginnen, den Schülern Matlab als ihren "Rechner" vorzustellen, ist der Übergang zur "echten" Programmierung viel schwieriger, da ihr erster Instinkt darin besteht, so viel wie möglich mit Matlab zu tun, bevor sie das Schiff verlassen, und dann ihre schlechten Gewohnheiten sind tief verwurzelt und zögern, eine neue Sprache zu lernen. Im Gegensatz dazu, wenn Sie mit Julia als Ihrem verherrlichten Taschenrechner beginnen, haben Sie eine viel größere Auswahl an Optionen, wenn es darum geht, ernsthafter zu programmieren. Sie müssen ihnen nicht beibringen, alles in "Vektor"-Operationen zu stopfen oder sie zu zwingen, Dinge schlecht zu machen, bevor sie es richtig machen.

Willst du damit sagen, dass ich Julia nicht in meinem Linear-Algebra-Kurs verwenden sollte ? Oder dass ich es nur nutzen sollte, wenn ich bereit bin, neben der Linearen Algebra auch Informatik zu unterrichten?

Ich stimme @stevengj sowohl beim Problem (das Unterrichten für Nicht-Programmierer wird viel schwieriger) als auch bei der Lösung (damit die Dinge in der REPL und den verschiedenen IDEs funktionieren) zu. Das Einfügen eines Skripts hätte immer noch die Julia 1.0-Bereichsregeln, aber das ist weniger wichtig, man muss nur darauf achten, dass die Klasse "wir können unsere for-Schleife in eine Funktion einfügen und dann die Funktion aufrufen" vor der Klasse "wir können setzen" unsere for-Schleife in einer Datei und binden die Datei"-Klasse ein.

Das klingt nach einem guten Kompromiss, da interaktives Debugging beim REPL nicht schmerzhafter wird als nötig (oder für neue Benutzer verwirrender), während normaler Code in Skripten strengen Scoping-Regeln folgen muss und vor Fehlern geschützt ist, die einige überschreiben Variablen versehentlich.

Willst du damit sagen, dass ich Julia nicht in meinem Linear-Algebra-Kurs verwenden sollte? Oder dass ich es nur nutzen sollte, wenn ich bereit bin, neben der Linearen Algebra auch Informatik zu unterrichten?

Vielleicht haben Sie das, was ich sagte, falsch verstanden (oder ich habe es nicht klar ausgedrückt). Ich sprach von Kursen, die Julia verwenden, um etwas domänenspezifisches zu unterrichten (zB habe ich Wirtschaftsstudenten numerische Methoden beigebracht), nicht von CS-Kursen (mit denen ich keine Erfahrung habe).

Der Punkt, den ich zu machen versuchte, ist, dass es vernünftig ist, einen gewissen Unterschied zwischen Julia und der Sprache X (die Matlab sein kann) zu erwarten; Umgekehrt kann (und wird) das Ignorieren zu Problemen führen.

Beim Erlernen einer neuen Sprache ziehe ich es persönlich vor, mich diesen Problemen frühzeitig zu stellen; Außerdem denke ich, dass Einfachheit und Konsistenz der Sprachsemantik auf lange Sicht wichtiger sind als die Ähnlichkeit mit anderen Sprachen. Aber ich erkenne diese Vorlieben als subjektiv an, und vernünftige Menschen können unterschiedliche haben.

Ich habe das (unregistrierte) Paket https://github.com/stevengj/SoftGlobalScope.jl erstellt

Wenn dies vernünftig erscheint, kann ich das Paket registrieren und dann standardmäßig in IJulia verwenden (und möglicherweise PRs an Juno usw. senden).

Der Punkt, den ich zu machen versuchte, ist, dass es vernünftig ist, einen gewissen Unterschied zwischen Julia und der Sprache X (die Matlab sein kann) zu erwarten.

Offensichtlich. Wenn ich sage "Benutze Julia anstelle von Matlab", meine ich nicht, dass ich versuche, ihnen die Matlab-Syntax in Julia beizubringen, noch ziele ich speziell auf ehemalige Matlab-Benutzer ab.

Ich ziehe es vor, mich diesen Problemen frühzeitig zu stellen

Es geht nicht um Unterschiede zu Matlab per se. Ich würde wirklich lieber nicht über den globalen vs. lokalen Geltungsbereich und den Nutzen eines global Schlüsselworts für die statische Analyse sprechen, wenn ich zum ersten Mal eine Schleife vor Nicht-CS-Studenten schreibe oder wenn sie zum ersten Mal Code aus a einfügen Funktion in die REPL ein, um es interaktiv auszuprobieren. Ich würde mich lieber auf die Mathematik konzentrieren, die ich mit der Schleife ausdrücken möchte.

Niemand argumentiert hier für einen weichen interaktiven Bereich, nur weil dies das ist, was Matlab-Benutzer erwarten. Abschweifungen in das ungewohnte Konzept des "Umfangs" sicher jede Nicht-CS-Vorlesung, in der Sie zum ersten Mal eine Schleife zeigen global Schlüsselwörter hinzuzufügen.)

Ein weiterer Fix, der hier nicht erwähnt wird, besteht darin, einfach aufhören zu müssen, dass 'for' einen Scope-Block definiert (einfach funktionieren und lassen würde einen neuen Scope erstellen).

@vtjnash , ich würde diese Diskussion lieber auf Dinge konzentrieren, die wir vor Julia 2.0 tun können. Ich stimme zu, dass ein anderes Verhalten im interaktiven Modus nur eine Notlösung ist, und wir sollten ernsthaft in Erwägung ziehen, die Scoping-Regeln in ein paar Jahren zu ändern.

Guter Punkt, das braucht auch import Future.scope 😀

(Ich denke, dieses Modul/Namespace/Verhaltenseffekt ist bereits reserviert/existiert)

Zur Erinnerung: Die Änderung sollte sicherstellen, dass sich der Code in allen Umgebungen mit globalem Gültigkeitsbereich gleich verhält, unabhängig davon, was zuvor in diesem Modul ausgewertet wurde. Vor dieser Änderung konnten Sie völlig unterschiedliche Antworten (aufgrund unterschiedlicher Bereichszuweisungen) erhalten, indem Sie einfach denselben Code zweimal ausführen oder in einer Datei verschieben.

Vor dieser Änderung konnten Sie völlig unterschiedliche Antworten (aufgrund unterschiedlicher Bereichszuweisungen) erhalten, indem Sie einfach denselben Code zweimal ausführen oder in einer Datei verschieben.

Die Zahl der Beschwerden, die ich diesbezüglich in der Praxis gesehen habe (null), wird sicherlich von der Zahl der Beschwerden und der Verwirrung, die Sie über das aktuelle Verhalten sehen (und bereits sehen), in den Schatten gestellt werden.

Vor dieser Änderung konnten Sie völlig unterschiedliche Antworten (aufgrund unterschiedlicher Bereichszuweisungen) erhalten, indem Sie einfach denselben Code zweimal ausführen

Meinen Sie, dass im folgenden Code a zwischen der ersten und der zweiten for Schleife wechselt? Meiner Meinung nach ist das erwartetes Verhalten, kein Fehler.

a = 0
for i = 1:5
  a += 1
end

for i = 1:5
  a += 1
end

Was würde der vorgeschlagene REPL-Modus mit enthaltenen Skripten tun?

@mauro3 @stevengj Ich nehme an, das Hinzufügen einer Funktion (sagen wir exec("path/to/script.jl") ) kann mit nur einem kleinen Versionsstoß erfolgen? Wir können auch warnen, dass exec andere Datei aus dem exec -Skript erstellt und dann einige pädagogische Botschaften dort ablegt, um sie dazu zu bringen, include .

Einige Gedanken, die ich letzte Nacht aufgeschrieben habe, als ich versuchte, mich (wieder einmal) mit diesem Thema auseinanderzusetzen, um herauszufinden, was die beste Vorgehensweise sein könnte. Keine Schlussfolgerung, aber ich denke, dies legt das Problem ziemlich klar fest. Nachdem ich einige Jahre über dieses Thema nachgedacht habe, glaube ich nicht, dass es eine "ideale Lösung" gibt - dies könnte eines dieser Probleme sein, bei denen es nur suboptimale Entscheidungen gibt.


Die Leute betrachten den globalen Geltungsbereich naiv als eine komische Art, die den lokalen Geltungsbereich einschließt. Aus diesem Grund funktionierten globale Scopes so wie in Julia 0.6 und früheren Versionen:

  • Wenn ein äußerer lokaler Gültigkeitsbereich eine lokale Variable erstellt und ihr ein innerer lokaler Gültigkeitsbereich zugewiesen wird, aktualisiert diese Zuweisung die äußere lokale Variable.
  • Wenn ein äußerer globaler Gültigkeitsbereich eine globale Variable erstellt und ihr ein innerer lokaler Gültigkeitsbereich zugewiesen wird, hat diese Zuweisung zuvor die äußere globale Variable aktualisiert.

Der Hauptunterschied ist jedoch:

  • Ob eine äußere lokale Variable konstruktionsbedingt vorhanden ist, hängt nicht von der Reihenfolge des Erscheinens oder der Ausführung der Ausdrücke im äußeren lokalen Gültigkeitsbereich ab.
  • Ob eine globale Variable existiert, kann jedoch nicht unabhängig von der Reihenfolge sein, da Ausdrücke im globalen Gültigkeitsbereich einzeln ausgewertet werden.

Da globale Gültigkeitsbereiche oft ziemlich lang sind – nicht selten über mehrere Dateien verteilt – ist die Bedeutung eines Ausdrucks außerdem von anderen Ausdrücken in beliebiger Entfernung davon abhängig, ein „gruseliger Fernwirkungseffekt“ und daher ziemlich unerwünscht .


Diese letzte Beobachtung zeigt, warum es problematisch ist, dass sich die beiden verschiedenen Versionen einer for-Schleife im globalen Geltungsbereich unterschiedlich verhalten:

# file1.jl
for i = 1:5
  a += 1
end
# file2.jl
a = 1



md5-f03fb9fa19e36e95f6b80b96bac9811e



```jl
# main.jl
include("file1.jl")
include("file2.jl")
include("file3.jl")

Beachten Sie auch, dass die Inhalte von file1.jl und file3.jl identisch sind und wir das Beispiel vereinfachen könnten, indem wir dieselbe Datei zweimal mit jeweils anderer Bedeutung und unterschiedlichem Verhalten einfügen.

Ein weiterer problematischer Fall ist eine lang andauernde REPL-Sitzung. Versuchen Sie ein Beispiel von irgendwo online? Es schlägt fehl, weil Sie zufällig eine globale Variable mit demselben Namen haben, den das Beispiel für eine lokale Variable in einer for-Schleife oder einem ähnlichen Konstrukt verwendet. Die Vorstellung, dass das neue Verhalten das einzige ist, das Verwirrung stiften kann, ist also definitiv nicht richtig. Ich stimme zu, dass das neue Verhalten ein Usability-Problem in der REPL ist, aber ich möchte nur das Gespräch mäßigen und die andere Seite hier klar darstellen.

Mein kleiner Vorschlag, der sich nicht mit dem Repl-Problem beschäftigt, aber für didaktische Zwecke nützlich wäre, wenn die Sprache nicht interaktiv unterrichtet wird, zumindest: einen Hauptblock namens "Programm" definieren, wie es in Fortran möglich ist (es ist der wie bei "let...end" oben, nur mit einer natürlicheren Schreibweise):

Programmtest
...
Ende

man könnte die Sprache unterrichten, ohne auf die Einzelheiten des Umfangs einzugehen und diesen Punkt erst schließlich diskutieren.

Ein weiterer problematischer Fall ist eine lang andauernde REPL-Sitzung. Versuchen Sie ein Beispiel von irgendwo online? Es schlägt fehl, weil Sie zufällig eine globale Variable mit demselben Namen haben, den das Beispiel für eine lokale Variable in einer for-Schleife oder einem ähnlichen Konstrukt verwendet.

Wie viele Beschwerden über Mailinglisten und Github-Probleme wurden diesbezüglich von verärgerten Benutzern eingereicht? Null, nach meiner Zählung. Wieso den? Wahrscheinlich, weil dieses Verhalten für die Menschen im Grunde nicht überraschend ist – wenn Sie global arbeiten, sind Sie vom globalen Zustand abhängig.

Die Vorstellung, dass das neue Verhalten das einzige ist, das Verwirrung stiften kann, ist also definitiv nicht richtig.

Ich denke, dies ist eine falsche Äquivalenz – es gibt hier eine große Diskrepanz im Grad der möglichen Verwirrung . In Julia 0.6 konnte ich einem Schüler Ihr Beispiel in Sekundenschnelle erklären: "Oh, siehe diese Schleife hängt von a , die Sie hier geändert haben." In Julia 1.0 mache ich mir ehrlich gesagt Sorgen darüber, was ich tun werde, wenn ich mitten in einer Vorlesung über lineare Algebra bin und auf mysteriöse Weise ein global Schlüsselwort vor Studenten tippen muss, die das Wort noch nie gehört haben "Scope" im Sinne von CS.

wir sollten ernsthaft in Erwägung ziehen, die Scoping-Regeln in einigen Jahren zu ändern.

Absolut nicht. Wollen Sie ernsthaft in die Welt vor v0.2 (siehe #1571 und #330) des Loop-Bereichs zurückkehren?

Wir haben das zeilenweise Kopieren und Einfügen von Code aus einer Funktion in die REPL nie vollständig unterstützt. Wir können dies also als Chance sehen, dass dies funktioniert. Insbesondere, während es für for Schleifen "funktionierte", funktionierte es nicht für innere Funktionen:

x = 0
f(y) = (x=y)

Innerhalb einer Funktion mutiert f das x aus der ersten Zeile. In der REPL wird es nicht. Aber mit einer solchen Transformation in SoftGlobalScope.jl könnte es funktionieren. Natürlich möchten wir dies wahrscheinlich nicht standardmäßig, da das Einfügen von eigenständigen Funktionsdefinitionen dann nicht funktionieren würde. Das erste, was mir in den Sinn kommt, ist ein REPL-Modus zum zeilenweisen Debuggen von Funktionen.

Möchtest du ernsthaft in die Welt vor v0.2 zurückkehren?

Nein, ich möchte zurück in die 0.6-Welt. 😉

Ich glaube, ich habe eher geantwortet auf:

Ein weiterer Fix, der hier nicht erwähnt wird, besteht darin, einfach damit aufzuhören, 'for' einen Scope-Block zu definieren

Wir haben das zeilenweise Kopieren und Einfügen von Code aus einer Funktion in die REPL nie vollständig unterstützt. Wir können dies also als Chance sehen, dass dies funktioniert.

Ich schätze dieses Gefühl sehr und für meine Anwendungsfälle würde es wirklich helfen. Aus meiner Sicht geht es wirklich darum, die REPL so nützlich wie möglich zu machen, anstatt die Scoping-Regeln der Sprache direkt zu ändern.

Das heißt, je mehr ich über dieses Problem nachdenke, desto mehr sehe ich die widersprüchlichen Ansichten, die ich (persönlich) habe, was die REPL tun sollte.

Konkret würde ich es sehr begrüßen, wenn die REPL den Bereichsregeln eines Funktionskörpers entspricht; dh Variablen sind eher lokal als global und Sie können Code einfach direkt aus einer Funktion kopieren und einfügen und wissen, dass er funktioniert. Ich kann mir vorstellen, dass eine naive Implementierung so etwas wie Let-Block Wrapping (wie bereits erwähnt) des Formulars wäre

julia> b = a + 1

verwandelt werden in

let a = _vars[:a]::Float64 # extract the variables used from the backing store
    # Code from the REPL
    b = a + 1
    # Save assigned variables back to the backing store
   _vars[:b] = b
end

Richtig gemacht (dh von jemandem, der weiß, was er tut), stelle ich mir vor, dass dies eine Reihe von Vorteilen gegenüber dem bestehenden REPL hätte. 1. Bisherige Workflows mit interaktiver Datenanalyse/-berechnung funktionieren einfach. 2. viel weniger Beiträge im Diskurs, in denen die grundlegende Antwort lautet: "Stoppt das Benchmarking mit globalen Variablen" - alles wäre lokal und damit hoffentlich schnell! :) 3. Kopieren und Einfügen in/von einem Funktionsrumpf funktioniert wie erwartet. 4. Eine workspace() ähnliche Funktion ist trivial, wenn der Hintergrundspeicher eine Art Dict ist; lösch es einfach aus. 5. Globale werden explizit - Dinge sind lokal, es sei denn, Sie bitten ausdrücklich darum, global zu sein; Das ist aus meiner Sicht ein großer Vorteil, ich mag es nicht, implizit Globals zu erstellen. Ein sehr kleiner letzter Punkt (und ich zögere, dies hinzuzufügen!) Dies würde dem Verhalten von Matlab entsprechen, was den Übergang für Leute erleichtert - bei der Matlab-REPL scheinen alle Variablen lokal zu sein, es sei denn, sie sind explizit als global annotiert.

Bis vor ein paar Stunden klang diese Geschichte für mich großartig. Aber nach Jeffs Kommentar zu Funktionen dachte ich darüber nach, eigenständige Funktionsdefinitionen einzufügen und wie dieser Ansatz dies grundsätzlich verhindern würde, da Funktionsdefinitionen in den globalen Geltungsbereich fallen sollten (zumindest ist das wahrscheinlich beabsichtigt); aber was dann , wenn sie dazu bestimmt , in dem lokalen Bereich zu gehen (eine innere Funktion)? Es gibt keine Informationen, die die beiden Möglichkeiten eindeutig machen. Es scheint, dass zwei REPL-Modi benötigt werden, einer mit lokalem Geltungsbereich und ein globaler Geltungsbereich. Das kann einerseits sehr verwirrend sein (man stelle sich die Diskurs-Beiträge vor...), andererseits aber auch sehr nützlich. (Beide REPL-Modi zu haben wäre auch nicht brechend, da Sie nur neue Funktionen einführen :))

Sich für das Zwischenhaus von SoftGlobalScope.jl könnte am Ende der am wenigsten verwirrende Kompromiss sein, aber meine Sorge ist, dass es sich nur um eine weitere Reihe von Regeln handelt, die man sich merken muss (welche Dinge in der REPL funktionieren, aber nicht in meinem Funktionskörper / globalen Geltungsbereich und und umgekehrt).

Entschuldigung für den langen Beitrag, aber ich denke, das ist wichtig für die Benutzerfreundlichkeit (und es hat mir geholfen, es zu überdenken!).

Wie viele Beschwerden über Mailinglisten und Github-Probleme wurden diesbezüglich von verärgerten Benutzern eingereicht? Null, nach meiner Zählung. Wieso den? Wahrscheinlich, weil dieses Verhalten für die Menschen im Grunde nicht überraschend ist – wenn Sie global arbeiten, sind Sie vom globalen Zustand abhängig.

Hmm, hast du das wirklich systematisch untersucht? Das muss mir entgangen sein. Dies bedeutet jedoch nicht, dass dieses Verhalten keine Quelle von Fehlern oder unerwarteten Ergebnissen ist; nur dass, nachdem der Benutzer es herausgefunden hat, es als korrektes Verhalten erkannt wurde und somit kein Problem/Beschwerde ausgelöst wurde.

In Julia 1.0 mache ich mir ehrlich gesagt Sorgen darüber, was ich tun werde, wenn ich mitten in einer Vorlesung über lineare Algebra bin und auf mysteriöse Weise ein globales Schlüsselwort eingeben muss

Ich habe Verständnis für dieses Problem. Wenn ich Studenten einfache Programmierung beibrachte, die für einen Kurs notwendig sind, schlug ich normalerweise vor, dass sie zwischen dem Einpacken von Code in Funktionen und dem Auskommentieren von function und end und her gehen und Dinge ausführen im globalen Rahmen, damit sie das Geschehen inspizieren können. Dies machte den Mangel an Debugging-Infrastruktur zu dieser Zeit in Julia ziemlich wett.

Es scheint, dass dieser Ansatz nicht mehr praktikabel ist. Aber ich frage mich, ob es wirklich der richtige Weg war, und in der Zwischenzeit haben sich verschiedene Dinge stark verbessert (#265 wurde behoben, Revise.jl und kürzlich Rebugger.j haben den Workflow/Debugging erheblich verbessert).

Es scheint, dass dieses Problem erfahrene Benutzer nicht sehr stört, die Hauptsorge ist die Verwirrung in einem pädagogischen Umfeld. Ich selbst habe damit noch nicht experimentiert, aber ich frage mich, ob wir stattdessen unsere Lehransätze anpassen könnten, zB Funktionen vor Schleifen einführen, Schleifen im globalen Geltungsbereich vermeiden. Dies sind sowieso Elemente von gutem Stil und würden den Schülern zugutekommen.

Nur eine kleine Anmerkung: Während eine spezielle Umschreibung des globalen Geltungsbereichs der REPL das Kopieren und Einfügen von Code in und aus Funktionen ermöglicht, wird das Kopieren und Einfügen in/aus dem globalen Geltungsbereich eines anderen Moduls nicht zugelassen.

Ich frage mich, ob wir stattdessen unsere Lehransätze anpassen könnten, zB Funktionen vor Schleifen einführen, Schleifen im globalen Geltungsbereich vermeiden.

Dies ist in einer Klasse, die sich nicht auf das Lehren von Programmieren konzentriert, völlig unpraktisch. Ich könnte Julia auch nicht in meinem Unterricht verwenden, wenn ich sie nicht interaktiv nutzen kann und/oder erst Funktionen für alles schreiben muss.

(Und es ist nicht nur pädagogisch. Schleifen im globalen Umfang sind für interaktives Arbeiten nützlich . Und einer der Hauptgründe, warum Leute dynamische Sprachen für technisches Computing mögen, ist ihre Fähigkeit zur interaktiven Erforschung. Nicht jede Codierung ist leistungsorientiert.)

Im Laufe der Jahre gab es Dutzende von Threads und Problemen, in denen die Leute verwirrt sind oder sich über die alte Unterscheidung zwischen "weichem / hartem Umfang" beschweren. nicht wahr. Ich könnte einige davon ausgraben, aber du warst da, @stevengj , also kannst du sie genauso leicht ausgraben und es fällt mir schwer zu glauben, dass du diese Beschwerden und Gespräche nicht bemerkt hast oder dich nicht daran erinnerst.

@StefanKarpinski , ich beziehe mich speziell auf Leute, die sich beschweren, dass eine globale Schleife vom globalen Zustand abhängt. Ich kann mich nicht erinnern, dass sich jemand darüber beschwert hat, dass dies ein schlechtes Benehmen war, und ich kann auch keine Beispiele dafür finden.

Ich stimme zu, dass die Leute verwirrt waren, wann und wo Zuweisungen neue Variablen definieren, aber normalerweise ging es in die andere Richtung – sie wollten, dass lokale Gültigkeitsbereiche globaler wirken (anstatt umgekehrt) oder keinen Unterschied zwischen begin und let . IIRC, die Beschwerde war nie, dass das Zuweisen einer globalen Variablen in einer globalen Schleife den überraschenden Nebeneffekt hatte, eine globale Variable zu ändern.

Das ganze Thema Scoping ist für neue Benutzer verwirrend und wird es auch weiterhin sein. Aber der verwirrende Teil waren nicht Fälle, in denen die Zuweisung eines globalen Variablennamens den globalen Status beeinflusste. Das aktuelle Verhalten macht dies schlimmer, nicht besser.

@StefanKarpinski : Ich habe das Gefühl, dass die Verwechslung mit Soft/Hard-Scope früher eher theoretischer Natur war (von Leuten, die das Handbuch lesen) als praktischer (von Leuten, die unerwartete Ergebnisse erzielten). Bei mir war das auf jeden Fall so und was zB die Suchergebnisse hier belegen; Ich habe hier ein Gegenbeispiel gefunden .

Andererseits wird dieses neue Verhalten die Leute nicht beim Lesen des Handbuchs verwirren, sondern bei der Verwendung des REPL. Letzteres ist wohl schlimmer.

SoftGlobalScope.jl ist jetzt ein registriertes Paket. Ich beabsichtige, es zumindest in diesem Semester standardmäßig für IJulia zu aktivieren (Opt-Out).

@mauro3 , sogar in Ihrem "Gegenbeispiel" geht es um jemanden, der durch harten Bereich, nicht durch weichen Bereich, verwirrt ist. Wenn Sie in 0.7 mehr Bereiche "schwer" machen, wird diese Art von Verwirrung mit Sicherheit noch mehr aufkommen.

Ich möchte darauf hinweisen, dass IJulia die interessante Möglichkeit hat, Variablen standardmäßig zu lokalen Do-Blöcken zu machen. Dh wenn Sie dies in einem einzigen Block tun, dann funktioniert es:

t = 0
for i = 1:n
    t += i
end
t

... und t ist nur innerhalb dieses Bewertungsblocks sichtbar. Wenn Sie möchten, dass es draußen sichtbar ist, müssen Sie Folgendes tun:

global t = 0
for i = 1:n
    global t += i
end
t

Ich habe auch einen ähnlichen Ansatz für Julia in Betracht gezogen, bei dem die Blöcke Dateien und nicht Module sind. Mit anderen Worten, wenn Sie nur t = 0 im obersten Bereich ausführen, wird eine Variable erstellt, die dateilokal und nicht global ist. Um eine wirklich globale Variable zu deklarieren, müssten Sie global t = 0 schreiben, die dann im gesamten Modul sichtbar wäre. Vielleicht zu seltsam, aber es ist mir im Laufe der Jahre oft eingefallen.

IJulia hat die interessante Möglichkeit, Variablen standardmäßig zu lokalen Do-Blöcken zu machen

@StefanKarpinski , ich denke, das wäre noch verwirrender und würde der normalen Verwendung von Notebooks global Schlüsselwort für alle Variablen zwischen Zellen zu benötigen – es würde noch mehr Diskussionen über Gültigkeitsbereichskonzepte erfordern als das Problem mit for Schleifen, die wir hier besprochen haben.

Ich denke, solange wir uns alle einig sind --- wie es scheint ---, dass dies hauptsächlich oder vollständig eine Frage der Interaktion ist, haben wir einen Weg nach vorne. Wenn wir dies in der REPL als Sonderfall verwenden (wie es bei IJulia der Fall ist), besteht der einzige schlechte Fall darin, etwas in der REPL zu entwickeln und es dann in den Skriptcode der obersten Ebene zu verschieben. Das ist wohl der Punkt, an dem Sie Funktionen einführen sollten, also finde ich es nicht so schlimm. Das Kopieren und Einfügen von Code zwischen den REPL- und Funktionskörpern wird (meistens) funktionieren, was wahrscheinlich gut genug ist.

Dann haben wir auch die Möglichkeit, die Unterscheidung weiter zu begründen/verdeutlichen, indem wir REPL-Variablen irgendwie lokal zur REPL machen --- dh keine normalen globalen Variablen, nicht verfügbar als Main.x . Dies ist dem, was @StefanKarpinski gerade oben vorgeschlagen hat, sehr ähnlich, wird jedoch von allen Eingabeblöcken /

Aus praktischer Sicht ist es nicht möglich, dies in der REPL "festzumachen".
nur wichtig für Lehrende/Nicht-Programmierer. Dieses Verhalten auch
macht interaktives Debuggen über die REPL (durch Kopieren und Einfügen von Teilen) sehr
unpraktisch. Dieser Debugging-Modus kann manchmal vorzuziehen sein (sogar einem
guter Debugger und) auch für erfahrene Programmierer (und ist oft einer von)
die Gründe, eine dynamische Sprache zu bevorzugen). Natürlich für Erfahrene
Programmierer, optional zu sein sollte kein Problem sein; Für unerfahrene Benutzer es
wäre vorzugsweise die Standardeinstellung.

@StefanKarpinski
Als naiver Programmierer verstehe ich nicht wirklich, was daran so falsch ist, die
globaler Geltungsbereich als eine lustige Art, die den lokalen Geltungsbereich einschließt, insbesondere in dynamischen
Sprachen. Ich verstehe, dass es aus Compiler-Sicht nicht so ist
unbedingt richtig (in Julia), aber es ist ein schönes, einfaches und nützliches Modell
für einen (naiven) Programmierer. (Ich vermute auch, dass es tatsächlich so implementiert werden könnte in
einige Sprachen).
Julia scheint es dem Programmierer auch so zu präsentieren:
Die folgende Funktionsfunktion gibt den Fehler "a nicht definiert" aus, der
es funktioniert nicht, wenn a=1 vor der for-Schleife steht.

Funktionstest()
für i = 1:10
a=a+i
Ende
a=1
@show a
Ende

was, wenn ich es nicht völlig missverstanden habe, im Widerspruch zu "Ob an
äußere lokale Variable existiert konstruktionsbedingt, hängt nicht von der Reihenfolge von ab
Erscheinen oder Ausführen der Ausdrücke im äußeren lokalen Geltungsbereich".

Ich bin sehr damit einverstanden, "spukhafte Fernwirkungen" zu vermeiden, und vieles
bevorzugen eine explizite Definition für die Verwendung von Globals im Funktions-/Aufrufstack
Ebene und möchte persönlich auch so etwas wie das Laden aus einer Datei in
eigenen Geltungsbereich und erfordert eine explizite Definition für die Verwendung globaler Variablen.
Auf der Ebene der Loops geht es mir allerdings etwas zu weit, da die
Definitionen/Kontext ist normalerweise ziemlich nahe.
Das 3-Dateien-Beispiel ist etwas konstruiert (und scheitert mit dem erwarteten "a not
definierte" Fehler): Normalerweise würden Sie die Anfangsdefinition in die gleiche setzen
Datei.
Darin liegt tatsächlich eine gruselige Gefahr (und ich wurde davon gebissen
in anderen Sprachen), in denen Includes im globalen Geltungsbereich ausgeführt werden, so dass Sie
definieren versehentlich eine globale Variable, die andere stören kann
Code. Die Verwendung von global in der Schleife ist jedoch keine Lösung für
dieses Problem.

in Bezug auf die lang andauernde REPL-Sitzung:
Das aktuelle Verhalten ersetzt einen sehr seltenen und leicht zu erkennenden Fehlermodus
zum Ausführen eines Online-Beispiels in der REPL (Sie vermissen das Kopieren/Einfügen der
initiale Definition der Variablen vor der Schleife und haben bereits die
gleiche Variable global definiert von etwas Vorherigem) mit nicht sein
in der Lage, ein Online-Beispiel korrekt auszuführen, wenn es Teil einer Funktion ist
(ohne überall global hinzuzufügen) und das Problem nicht zu lösen, wenn es so ist
nicht (wenn global bereits im Online-Code vorhanden ist, verwenden Sie weiterhin die
falscher Wert in der bereits vorhandenen globalen Variablen)

Ich hätte mich früher darauf einstellen sollen, aber nach einem kurzen Moment der Besorgnis scheint alles in Ordnung zu sein.

Wir haben das zeilenweise Kopieren und Einfügen von Code aus einer Funktion in die REPL nie vollständig unterstützt ... Das erste, was mir in den Sinn kommt, ist ein REPL-Modus zum zeilenweisen Funktionsdebuggen.

Tatsächlich funktioniert Rebugger (was genau das ist) nur auf 1.0 richtig, weil ihm die Bereichsverschlechterung von 0.7 fehlt und es nie dazu gebracht werden könnte, auf 0.6 zu funktionieren. Ich freue mich jedoch, bestätigen zu können, dass SoftGlobalScope.jl dies nicht zu brechen scheint. Wenn Sie beispielsweise tief genug in show([1,2,4]) einsteigen, erhalten Sie hier:

show_delim_array(io::IO, itr::Union{SimpleVector, AbstractArray}, op, delim, cl, delim_one) in Base at show.jl:649
  io = IOContext(Base.TTY(RawFD(0x0000000d) open, 0 bytes waiting))
  itr = [1, 2, 4]
  op = [
  delim = ,
  cl = ]
  delim_one = false
  i1 = 1
  l = 3
rebug> eval(softscope(Main, :(<strong i="10">@eval</strong> Base let (io, itr, op, delim, cl, delim_one, i1, l) = Main.Rebugger.getstored("bbf69398-aac5-11e8-1427-0158b103a88c")
       begin
           print(io, op)
           if !(show_circular(io, itr))
               recur_io = IOContext(io, :SHOWN_SET => itr)
               if !(haskey(io, :compact))
                   recur_io = IOContext(recur_io, :compact => true)
               end
               first = true
               i = i1
               if l >= i1
                   while true
                       if !(isassigned(itr, i))
                           print(io, undef_ref_str)
                       else
                           x = itr[i]
                           show(recur_io, x)
                       end
                       i += 1
                       if i > l
                           delim_one && (first && print(io, delim))
                           break
                       end
                       first = false
                       print(io, delim)
                       print(io, ' ')
                   end
               end
           end
           print(io, cl)
       end
       end)))
[1, 2, 4]

Es funktioniert also gut auf 1.0 (mit oder ohne softscope ). Auf 0.7 ergibt die Auswertung (mit oder ohne softscope )

┌ Warning: Deprecated syntax `implicit assignment to global variable `first``.
│ Use `global first` instead.
└ @ none:0
┌ Warning: Deprecated syntax `implicit assignment to global variable `first``.
│ Use `global first` instead.
└ @ none:0
[ERROR: invalid redefinition of constant first
Stacktrace:
 [1] top-level scope at ./REBUG:9 [inlined]
 [2] top-level scope at ./none:0
 [3] eval(::Module, ::Any) at ./boot.jl:319
 [4] top-level scope at none:0
 [5] eval at ./boot.jl:319 [inlined]
 [6] eval(::Expr) at ./client.jl:399
 [7] top-level scope at none:0

0.7/1.0 sind also definitiv ein Fortschritt, und wenn softscope bestimmte Dinge einfacher macht, ohne wichtige Funktionen zu beeinträchtigen, ist das großartig.

Die größte Sorge besteht daher darin, dies angemessen abzufangen, ohne andere Pakete zu tanken (https://github.com/stevengj/SoftGlobalScope.jl/issues/2).

@timholy , SoftScope berührt die Argumente von Makroaufrufen nicht (da es keine Möglichkeit gibt, zu wissen, wie das Makro es umschreiben würde), also ist :(<strong i="6">@eval</strong> ...) geschützt.

scheint im Widerspruch zu "Ob ein
äußere lokale Variable existiert konstruktionsbedingt, hängt nicht von der Reihenfolge von ab
Erscheinen oder Ausführen der Ausdrücke im äußeren lokalen Geltungsbereich".

Die (äußere) lokale Variable a existiert, wurde aber noch nicht zugewiesen. Wenn die Schleife vor dem Lesen versucht hat, a zuzuweisen, wäre die Zuweisung auch außerhalb sichtbar.

Im Allgemeinen sind das Erstellen einer Variablenbindung und das Zuweisen eines Werts separate Schritte.

Wie ist der Zeitplan dazu? Es scheint eine große Verbesserung der Benutzerfreundlichkeit zu sein. Und zu dieser "kritischen" Zeit von Julia mit 1.0 erscheint es vorteilhaft, dies so schnell wie möglich zu beheben (wie von Jeff oben vorgeschlagen) und eine neue Julia-Version oder REPL-Version zu markieren. (Entschuldigung für diesen Sessel-Kommentar, da ich das sicherlich nicht reparieren werde!)

@JeffBezanson
Ich wollte argumentieren, dass dies zwar wahr ist (für die Implementierung/den Compiler), der naive Julia-Programmierer jedoch kein anderes Verhalten als sein einfacheres konzeptionelles Modell erkennen kann (eine Variable beginnt in dem Moment zu existieren, in dem sie definiert wird). Leider haben Sie Recht, der folgende Code gibt keinen Fehler aus, während er einen Fehler ausgibt, wenn Sie a=2 am Ende weglassen
Funktionstest()
für i = 1:10
a=1
Ende
println(a)
a=2
Ende
Ich erkläre das leider: Ich kann das Verhalten verstehen (weil ich schon einmal mit kompilierten Sprachen gearbeitet habe), finde es aber trotzdem verwirrend und unerwartet. Wie schlimm muss es für jemanden sein, der nur Erfahrung mit Skripten hat oder neu in der Programmierung ist. Außerdem habe ich Code gefunden, der das Verhalten zeigt, ich sehe keine nützliche Anwendung (vielleicht können Sie mir da helfen)

Auf der REPL:
Ich bin nur mehr davon überzeugt, dass das Zurücksetzen des Scopings auf "normal" zumindest in der REPL (keine Notwendigkeit, globale Schleifen hinzuzufügen) hohe Priorität hat: Ich habe heute einige Dinge in der REPL getestet und wurde (wieder) davon gebissen, etwas Zeit brauchen, um es zu erkennen. Da ich Julia schon seit einiger Zeit folge, sehr gerne, sogar diesen Thread über das Problem verfolge, würde ich es sogar als Showstopper bezeichnen: Ein Neuling (zu der Julia), der die Sprache testet, wird sehr wahrscheinlich nicht gefunden das Problem lösen und einfach aufgeben.

@jeffbezanson und ich sind beide im lang ersehnten Urlaub (ich sollte das nicht lesen). Wir können in einer Woche oder so herausfinden, was zu tun ist.

@derijkp , während das Feedback geschätzt wird, sind die Scoping-Regeln nicht für eine breitere Debatte oder Überarbeitung

@derijkp Eine kurze Antwort ist, dass es meiner Meinung nach einfacher ist, wenn der Gültigkeitsbereich einer Variablen einem Blockkonstrukt entspricht (z. B. dem Körper einer Funktion oder Schleife). Mit Ihrem Vorschlag wäre der Gültigkeitsbereich einer Variablen eine Teilmenge eines Blocks, was meiner Meinung nach letztendlich komplexer und verwirrender ist --- Sie können nicht auf eine syntaktische Form zeigen, die dem Gültigkeitsbereich der Variablen entspricht.

Ja, ich kann glauben, dass dies nicht mit der Intuition einiger Leute übereinstimmt. Aber Sie können nur die ersten zehn Minuten der Verwendung einer Sprache bis zu einem gewissen Punkt optimieren. Die eigentliche Frage ist, wie schwer es ist, die Funktionsweise zu lehren/zu erlernen und welches Design auf lange Sicht Zeit spart (indem die Sprache einfacher wird, die Entwicklung von Werkzeugen erleichtert usw.)?

(in Übereinstimmung mit vielen der obigen Ausführungen zur Änderung des Verhaltens der REPL)
Ich möchte, dass die REPL so ist, dass sie nicht zu dieser Stackoverflow-Frage führt
und am besten wäre es früher, da viele neue Augen auf Julia blicken

Ich stimme zu ... Und denke auch, dass sich die Scoping-Regeln nicht unbedingt ändern sollten, nur alle interaktiven Schnittstellen (dh die REPL-, Jupyter- und Juno-Steuerelementeingabe)

Dabei geht es nicht nur darum, dass Anfänger eine neue Regel lernen. Wenn Sie Codefragmente nicht in die REPL, Jupyter usw. und auch in Funktionen kopieren und einfügen können, ist dies auch für fortgeschrittene Programmierer ein großes Ärgernis.

Natürlich stimme ich auch den anderen Postern zu ... Anfänger nehmen Codefragmente, die sie in Funktionen sehen, kopieren I in Skripte und sind völlig verwirrt, wenn sie nicht das gleiche Verhalten haben, wenn sie in eine Funktion kopiert werden , in juno, repl und jupyter. Es wird 100 Fragen zum Austausch von Stapeln geben, die sich auf dasselbe Problem beziehen. Fortgeschrittene Programmierer werden alle möglichen hausgemachten Lösungen haben, die in let Blöcke usw. einpacken, was die Dinge noch weiter verwirren wird

Es wird 100 Fragen zum Austausch von Stapeln geben, die sich auf dasselbe Problem beziehen. Fortgeschrittene Programmierer werden alle möglichen hausgemachten Lösungen haben, die in let Blöcke usw. einpacken, was die Dinge noch weiter verwirren wird

Möglicherweise, aber in diesem Stadium ist dies hypothetisch (auch das OP der verknüpften Frage fragt nach den Gründen für die Scoping-Regel, anstatt darüber verwirrt zu sein).

Auch wenn ich die Unterrichtserfahrung aller respektiere, die diesbezüglich Bedenken haben, wird sich die Zeit zeigen, ob sich dies im Klassenzimmer als große Sache herausstellt.

der Fragesteller scheint davon verwirrt zu sein: "Ich frage mich, ob das für julia-Anfänger intuitiv ist. Für mich war es nicht intuitiv ..."

der Fragesteller scheint davon verwirrt gewesen zu sein:

Ganz zu schweigen davon, dass dies jemand ist, der eindeutig genug über Programmiersprachen weiß, um die Nuancen des Umfangs zu verstehen. Was ist mit all den Matlab-Benutzern, die diese Themen völlig ignorieren ... und wahrscheinlich nie genug Zeit investieren werden, um die Nuancen zu verstehen.

Möglicherweise, aber zu diesem Zeitpunkt ist dies hypothetisch

Ich habe bereits mehrere Fragen zu diesem Thema zu Stackoverflow beantwortet, hauptsächlich von neuen Benutzern und noch mehr im wirklichen Leben (die letzte erst gestern von einem Matlab-Benutzer, der dies als No-Go ansah).

Es wird 100 Fragen zum Austausch von Stapeln geben, die sich auf dasselbe Problem beziehen.

In meiner "Freizeit" habe ich den SE-Fragen die Tags scope , scoping und global-variables hinzugefügt. Ich höre nur aus Zeitmangel auf, nicht weil es nicht mehr gibt.

Fazit nach vielen Diskussionen einschließlich Triage: Wir werden etwas in der Art von SoftGlobalScope in Base aufnehmen und es in der REPL und allen anderen interaktiven Evaluierungskontexten verwenden. @JeffBezanson hat darauf hingewiesen, dass die Art und Weise, wie dies implementiert wird, im Wesentlichen die gleiche ist, wie zuvor Soft Scope implementiert wurde, sodass wir in gewissem Maße den Kreis schließen. Der Unterschied besteht darin, dass es jetzt kein Scope-Verhalten in Modulen oder Skripten gibt, sondern nur in REPL-ähnlichen Kontexten. Ich denke auch, dass das _Erklären_ von Soft Scopes als Source-Rewrite klarer ist als der Versuch, zwischen Hard- und Soft-Scopes zu unterscheiden (was wir nie so erklärt haben, wie Jeff es erklärt hat, möchte ich darauf hinweisen).

Diese beiden Aussagen verwirren mich etwas, da sie etwas widersprüchlich erscheinen:

und verwenden Sie es in der REPL und allen anderen interaktiven Bewertungskontexten

es gibt kein Scope-Verhalten in [...] Skripten, nur in REPL-ähnlichen Kontexten.

Bedeutet dies, dass das Modul Main manchmal einen weichen Gültigkeitsbereich hat (zB an der REPL-Eingabeaufforderung) und manchmal einen harten Gültigkeitsbereich (zB wenn julia -L script.jl )? Wäre es nicht sinnvoll zu sagen, dass Main immer einen weichen Geltungsbereich hat? Und ein Modul kann sich um using SoftGlobalScope für den weichen Umfang

(Ich schätze) Scoping-Regeln können in Skripten nicht geändert werden, da dies abwärtskompatibel wäre, dh das Versprechen brechen würde, dass jeder Code, der für 1.0 geschrieben wurde, auf jeder 1.*-Version läuft. Sie haben jedoch Recht, dass das gleiche Problem mit der Festlegung des Bereichs für die REPL auch für Skripte gilt (naive Benutzer sind völlig ratlos, warum ihr Code nicht richtig funktioniert, wenn er als Skript ausgeführt wird). Eine Möglichkeit, dieses Problem ohne größere Inkompatibilität zu lösen / zu lindern, wäre, der julia-Cmdline eine Option hinzuzufügen, um softscope (oder eine Alternative) zu verwenden, z. B. julia -f programfile, und diese Option in jeder Beschreibung / Anleitung anzuzeigen, die ein Anfänger wahrscheinlich wird rüberkommen.
Ich sehe auch eine mögliche Alternative für das Softscope, die einige Vorteile haben könnte (obwohl ich wahrscheinlich Nachteile übersehe): Was wäre, wenn eine Datei (ein aufgerufenes Skript) immer ihren eigenen lokalen Gültigkeitsbereich einführen würde: Die Bereichsregeln würden vollständig mit denen in übereinstimmen Funktionen und mit den Erwartungen vieler Benutzer. Es würde auch viele der Leistungsverpflichtungen bei neuen Benutzern beseitigen:
Keine unnötigen Globals mehr (Globals müssten explizit definiert werden) und Code könnte kompiliert werden
(Wie oft mussten Sie sagen, um alles in eine Funktion zu packen und die Verwendung von Globals zu vermeiden?)

Ich habe das gerade getroffen und war ehrlich gesagt völlig verblüfft, da ich es noch nie in einer anderen Sprache gesehen habe. Ich plane, noch in diesem Jahr einen optionalen Julia-Kurs für fortgeschrittene R-Anwender an meiner Uni einzuführen, sobald sich die Dinge beruhigt haben, und meine Studenten werden diesen am Tag 0 erreichen, wenn sie anfangen, Dinge nach dem Zufallsprinzip in die REPL einzugeben. Und die Tatsache, dass sich for Schleifen anders verhalten als if Anweisungen, streut nur Salz in die Wunde, so logisch dies in Bezug auf den Umfang auch sein mag. Scope innerhalb von Funktionen ist schwer genug, um Biologiestudenten verständlich zu machen, die Idee, _wenn auch wahrgenommene_ eklatante Inkonsistenzen darin in der REPL / in einem Skript / in einer for-Schleife / in einer if-Anweisung erklären zu müssen (denn das ist, was wir reden .) hier) auf eine Art und Weise, die sich von jeder anderen Sprache der Welt unterscheidet, macht mich sehr traurig.

Ich verstehe das Versprechen der Abwärtskompatibilität, das gemacht wurde, aber diese Funktion zu haben _wie von jeder Nicht-CS-Person auf dem Planeten erwartet (und den meisten CS-Leute, die ich vermute)_ scheint eher ein Bugfix als ein Abwärtskompatibilitätsproblem zu sein - das sagen wir nicht dass jeder Fehler für immer reproduziert wird, oder? Der REPL-Fix ist offensichtlich wichtig, daher ist es großartig, dass Sie dies vorschlagen, aber dann erklären müssen, dass Sie kein Skript in die REPL kopieren und erwarten können, dass das gleiche Verhalten genauso schlimm oder schlimmer ist als das ursprüngliche Problem.

Bitte, bitte, denken Sie bitte daran, dies als Bugfix zu behandeln und es mit Skripten sowie der REPL herauszuschieben - auch wenn es einen Wechsel zum "alten" Verhalten gibt - und dies so schnell wie möglich in 1.0.1 zu tun.

Eine Kollegin, die ich versuchen wollte, Julia zu lernen, ist auch gerade darauf gestoßen. Die ganze Sache mit globalen vs. lokalen Variablen in den ersten Schritten erklären zu müssen, ist nicht ideal ...

Ich glaube nicht, dass dies als "Bugfix" behandelt wird, da es den Stabilitätsvertrag von 1.0 brechen würde. Es erscheint mir jedoch sinnvoll, Softscope für Skripte zu verwenden, die mit julia -i (dh im "interaktiven" Modus) ausgeführt werden.

(Das heißt, es gäbe ein Flag --softscope={yes|no} und es würde standardmäßig den Wert isinteractive .)

Wir müssen die Wahl des Skriptmodus berücksichtigen.

Übrigens ist es für mich nicht verrückt, für jedes "Skript", dh für julia foo.jl standardmäßig --softscope=yes zu verwenden und nur die "harten" Bereichsregeln für Module und include zu aktivieren

Übrigens ist es für mich nicht verrückt, für jedes "Skript" standardmäßig --softscope=yes zu verwenden.

Dass. Der andere, den Sie ernsthaft in Betracht ziehen sollten, ist Juno. Denken Sie daran, dass die Leute <shift-enter> ihren Code durchlaufen werden, um eine interaktive Entwicklung durchzuführen (insbesondere bei der Arbeit mit den Regressionstests) und später erwarten, dieselbe Datei ausführen zu können. Sollte es eine Rolle spielen, ob sich der Code in einem @testset oder nicht (was meiner Meinung nach einen Bereich einführen könnte)? Es wäre für den Benutzer sehr verwirrend, wenn sich derselbe Text in einem @testset ändert, im Gegensatz zu nicht, wenn die Atom-Integration verwendet wird, und es wäre auch unvereinbar mit ] test .

Es klingt für mich sicher nach der besten Lösung, dass der Hard-Scope einfach eine Opt-In-Sache ist, wenn jede andere Verwendung (einschließlich include in Skripten) softscope sofern Sie nichts anderes sagen .

anders als jede andere Sprache auf der Erde

Möchten Sie var x = 0 schreiben, um jede Variable einzuführen? Das würde dies auch "korrigieren" und eher anderen Sprachen ähneln.

Wir sagen nicht, dass jeder Fehler für immer reproduziert wird, oder?

So funktioniert das nicht. Sie können die gewünschte Sprache nicht ändern, indem Sie das aktuelle Verhalten einfach als Fehler bezeichnen.

Ich glaube wirklich nicht, dass es dafür eine Befehlszeilenoption geben sollte. Dann muss jeder Julia-Code einen Kommentar oder etwas enthalten, das Ihnen sagt, welche Option Sie verwenden sollen. Eine Art Parser-Direktive in einer Quelldatei wäre etwas besser, aber noch besser wäre es, eine feste Regel zu haben. Beispielsweise kann ein harter Geltungsbereich nur innerhalb von Modulen sinnvoll sein.

Lassen Sie mich noch einmal versuchen, eine Erklärung dafür zu geben, die nützlich sein könnte, um die Manie, Hysterie und das Gemetzel zu vermeiden, die Menschen im Klassenzimmer sehen:

"
Julia hat zwei Arten von Variablen: lokale und globale. Variablen, die Sie in der REPL oder auf der obersten Ebene einführen, abgesehen von allem anderen, sind global. Variablen, die innerhalb von Funktionen und Schleifen eingeführt werden, sind lokal. Das Aktualisieren globaler Variablen in einem Programm ist im Allgemeinen schlecht. Wenn Sie sich also in einer Schleife oder Funktion befinden und eine globale aktualisieren möchten, müssen Sie dies explizit angeben, indem Sie die Deklaration global erneut schreiben.
"

Vielleicht kann das verbessert werden; Vorschläge willkommen. Ich weiß, du bräuchtest lieber gar keine Erklärung. Ich verstehe das. Aber es kommt mir nicht so schlimm vor.

Ich glaube wirklich nicht, dass es dafür eine Befehlszeilenoption geben sollte. Dann muss jeder Julia-Code einen Kommentar oder etwas enthalten, das Ihnen sagt, welche Option Sie verwenden sollen. Eine Art Parser-Direktive in einer Quelldatei wäre etwas besser, aber noch besser wäre es, eine feste Regel zu haben

Ich stimme zu. Klingt für mich nach Lehr- und Kommunikationskopfschmerzen.

Beispielsweise kann ein harter Geltungsbereich nur innerhalb von Modulen sinnvoll sein.

Genau so verstehe ich: (! Nicht in einem Modul) , wenn ich ein kurzes Skript in einen hatte .jl - Datei , die ich von einem IJulia Notebook kopiert hatte, dann , wenn ich diesen Code lief entweder in der REPL direkt oder Schicht- in Juno eingeben, dann würde es sich konsistent als Soft-Scope verhalten... aber wenn ich es anstelle eines module Blocks kopierte, würde es mich über Globals anschreien? Aber wenn ich diesen Code in Funktionen innerhalb eines Moduls kopiert habe, sollte es funktionieren.

Wenn ja, macht das durchaus Sinn, ist sehr lehrreich und stimmig. Skripte der obersten Ebene sind eine interaktive Schnittstelle zum Erkunden usw., aber Sie würden diese Art von Code niemals in ein Modul einfügen. Module sind etwas, das Sie mit Funktionen füllen sollten, die sehr sorgfältig durchdacht sind. Es wäre einfach, den Leuten von diesen Regeln zu erzählen.

Möchten Sie var x = 0 schreiben, um jede Variable einzuführen? Das würde dies auch "korrigieren" und eher anderen Sprachen ähneln.

Nein, lieber nicht! Aber Skriptsprachen, die eine REPL haben, tun dies selten (zB Ruby, Python, R, ...), sie verhalten sich wie Julia v0.6.

Julia hat zwei Arten von Variablen: lokale und globale. Variablen, die Sie in der REPL oder auf der obersten Ebene einführen, abgesehen von allem anderen, sind global. Variablen, die innerhalb von Funktionen und Schleifen eingeführt werden, sind lokal. Das Aktualisieren globaler Variablen in einem Programm ist im Allgemeinen schlecht. Wenn Sie sich also in einer Schleife oder Funktion befinden und eine globale aktualisieren möchten, müssen Sie dies explizit machen, indem Sie die globale Deklaration erneut schreiben.

Ich verstehe vollkommen, was Sie hier sagen, und ich werde diesen Fehler (Holz anfassen!) nicht noch einmal machen. Aber das ganze Problem, um das ich mir Sorgen mache, bin nicht ich. Ich fand es relativ einfach, Scope einzuführen (ohne es direkt zu erwähnen), wenn ich erkläre, dass Variablen innerhalb von Funktionen diejenigen außerhalb nicht sehen können und umgekehrt (obwohl dies in R mehr ein Anspruch als eine Tatsache ist!), weil Funktionen selbst sind bereits ein _relativ_ fortschrittliches Konzept. Aber das trifft hier viel früher in der Lernkurve, wo wir nicht wollen, dass etwas so Kompliziertes wie die Reichweite auf die Menschen einwirkt...

Beachten Sie auch, dass es nicht nur "_Variablen, die Sie in der REPL oder auf der obersten Ebene einführen, außerhalb von allem anderen sind global_" und "_Variablen, die innerhalb von Funktionen und Schleifen eingeführt werden, sind lokal_", es sind auch Variablen in if-Anweisungen in der REPL oder at die oberste Ebene ist global, aber Variablen in @testset sind lokal. Wir landen in einem Kaninchenbau von "Probieren Sie es einfach aus und finden Sie selbst heraus, ob es lokal oder global ist, viel Glück".

Ich stimme jedoch @jlperla zu - der Vorschlag, dass "Hard Scope nur innerhalb von Modulen Sinn machen könnte" scheint mir völlig in Ordnung! Module sind wieder ein ausreichend fortgeschrittenes Konzept... wenn Softscope für die REPL und Skripte funktioniert, ist das völlig in Ordnung.

Wir wollen nicht, dass etwas so Kompliziertes wie die Reichweite auf die Leute einwirkt...
auf der obersten Ebene sind global, aber Variablen in @testset sind lokal

Was ich erreichen möchte, ist, dass ich der Meinung bin, dass eine einfache Beschreibung von Global vs. Local für den frühen Unterricht ausreichend ist --- Sie müssen nicht einmal das Wort "Scope" sagen (es kommt überhaupt nicht vor .) in meiner Erklärung oben). Wenn Sie nur einige einfache Ausdrücke und Schleifen in der REPL zeigen, bringen Sie den Leuten nichts über Testsätze bei und Sie brauchen keine erschöpfende Liste des Scoping-Verhaltens von allem in der Sprache.

Mein einziger Punkt ist, dass diese Änderung es nicht plötzlich notwendig macht, viele Details über die Sprache im Voraus zu vermitteln. Sie können immer noch die überwiegende Mehrheit der Dinge über Bereiche, Testsätze usw. ignorieren, und eine einfache Zeile über global vs. lokal sollte ausreichen.

und eine einfache Zeile über global vs. lokal sollte ausreichen.

In einer Welt, in der jeder angefangen hat, seinen gesamten Code von Grund auf neu zu schreiben, würde ich dem voll und ganz zustimmen.

Das Problem ist, dass Sie den Schülern nicht nur den Umfang beibringen müssen, sondern auch verstehen, woher sie Code kopieren und einfügen, von dem sie stammen. Sie müssen ihnen beibringen, dass sie beim Kopieren und Einfügen von Code, der sich im Stackexchange innerhalb einer Funktion oder eines Let-Blocks befindet, diesen durchsuchen und herausfinden müssen, wo sie "global" hinzufügen können, wenn sie ihn in die REPL oder ein .jl einfügen.

Und dann fangen die Schüler an zu fragen, warum for diesen Spielraum schafft, um den sie sich kümmern müssen, aber nicht um andere Dinge....

Wir landen in einem Kaninchenbau von "Probieren Sie es einfach aus und finden Sie selbst heraus, ob es lokal oder global ist, viel Glück".

Pop-Quiz: in julia 0.6 ist x global oder lokal:

for i = 1:10
    x = i
end

Die Antwort ist, dass es keine Möglichkeit gibt, dies herauszufinden, da es davon abhängt, ob zuvor ein globales x definiert wurde. Jetzt können Sie mit Sicherheit sagen, dass es lokal ist.

Leute, diese Diskussion grenzt daran, nicht mehr produktiv zu sein. Jeff weiß sehr gut, dass das alte Verhalten in der REPL nett war. Wer hat es Ihrer Meinung nach überhaupt entworfen und umgesetzt? Wir haben uns bereits verpflichtet, das interaktive Verhalten zu ändern. Es muss noch entschieden werden, ob ein "Skript" interaktiv ist oder nicht. Es klingt interaktiv, wenn Sie es "ein Skript" nennen, aber es klingt weit weniger interaktiv, wenn Sie es "ein Programm" nennen - doch sie sind genau dasselbe. Bitte halten Sie die Antworten kurz und konstruktiv und konzentrieren Sie sich auf die Dinge, die noch entschieden werden müssen. Davon abweichende Kommentare können ausgeblendet und der Thread gesperrt werden.

Ein Gedanke, den ich hatte, aber wir als "zu nervig" abgetan und "wahrscheinlich dazu führen, dass die Dorfbewohner ihre Mistgabeln herausholen" war, dass wir in nicht interaktiven Kontexten ein local oder global verlangen könnten

Als ich Julia zum ersten Mal kennenlernte (vor nicht allzu langer Zeit, und ich komme hauptsächlich aus Fortran), wurde mir beigebracht, dass "Julia auf Funktionsebene kompiliert und schnell ist, also muss alles, was effizient sein muss, innerhalb von Funktionen erledigt werden". . Im Haupt-'Programm' verhält es sich wie eine Skriptsprache". Ich fand das fair genug, da ich mir nicht vorstellen kann, dass jemand etwas zu rechenintensives macht, ohne diese Aussage zu verstehen. Wenn daher beim Hauptprogramm Leistungseinbußen für die Verwendung derselben Notation und Konstruktionen wie bei den Funktionen erforderlich sind, finde ich das völlig akzeptabel, viel akzeptabler, als zu versuchen, diese Scoping-Regeln zu verstehen und zu lehren und nicht in der Lage zu sein, zu kopieren und Codes von einem Ort zum anderen einfügen.

Übrigens, ich bin noch ein Neuling in Julia und habe es ausgewählt, um einigen High-School- und Bachelor-Studenten einige Grundlagen der Simulation physikalischer Systeme beizubringen. Und ich hüpfe bereits, dass dieses Problem zum "normalen" Verhalten früherer Versionen zurückkehrt, weil es uns ziemliche Kopfschmerzen bereitet.

Diese Konversation ist jetzt gesperrt und nur Julia Committer können Kommentare abgeben.

@JeffBezanson , was wäre der Plan, um die von Ihnen in diesem Diskursthread vorgeschlagene Semantik zu implementieren, zunächst nur in der REPL und an anderer Stelle?

Es hört sich so an, als ob Sie planen, dies direkt in den Senkungscode ( julia-syntax.scm ) zu stecken, anstatt die Syntax wie bei SoftScope.jl neu zu schreiben? Oder möchten Sie es lieber zuerst als Syntax-Rewriting haben (SoftScope an die vorgeschlagene Regel anpassen und in eine stdlib konvertieren) und das Einfügen in den niedrigeren Code für eine spätere Julia-Version verschieben?

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen