Hallo, ich habe hier folgende Funktion, die leider nicht das macht, was sie machen soll.
Sinn ist folgender: Manche Waffen haben einen eigenen Parameter im Name (gekennzeichnet durch !...) und durch den bekommt der Kämpfer neue Skills. Alle Angriffe sind so quasi von Waffen abhängig.
Unterschiedliche Waffen, unterschiedliche Parameter, unterschiedliche Angriffe.
Funktioniert auch alles soweit, Problem ist die for-Schleife zum Schluss:
Diese löscht das 0. Element aus dem Array skills_neu UND aus @skills. dadurch wird das eigentlich 1. Element zum 0. und dann übersprungen.
Das ist aber Quatsch, der soll das Element doch nur aus skills_neu löschen...
Warum löscht der das auch aus @skills?
Außerdem bin ich für Optimierungsvorschläge dankbar "
Arrays werden nicht mit dem Call-By-Value sondern mit dem Call-By-Reference Prinzip übergeben.
Wenn du schreibst:
Dann bedeutet dies, dass das Array welches die Fähigkeiten enthält nicht nur durch die Variable "@skills" sondern auch durch "skills_neu" referenziert wird.
Du musst dir das so vorstellen als ob es in deinem Speicher irgendwo einen Abschnitt gibt welcher dieses Array darstellt. Irgendein Ding mit Daten.
Und die Variablen "@skills" und "skills_neu" sind nur Zeiger welche dem Programm sagen an welcher Stelle diese Daten liegen.
Wenn du den Code schreibst dann sagst du dem Programm er soll den Zeiger in "skills_neu" auf "@skills" setzen. Er wird keine Kopie von den Inhalten anlegen.
Für dein Vorhaben solltest du entweder benutzen oder die Hilfsvariable "skills_neu" direkt vergessen und auf dem Array "@skills" arbeiten so wie es ist.
Was mich im Moment noch stutzig macht ist die eigenartige If-Bediengung Das macht nicht sonderlich viel Sinn den leeren String hier zu verwenden, jeder String enthält den leeren String. Das ist als ob du " if true " schreiben würdest nur aufwendiger...
Was ich ebenfalls nicht ganz verstehe ist folgender Part:
Erstens muss die Signatur der For-Schleife eher so aussehen:
Da 2 Punkte ("..") und 3 Punkte ("...") eine andere Semantik haben.
Dies hier: "for i in 0..4" würde die Zahlen 0, 1, 2, 3 und 4 iterieren, wobei "for i in 0...4" nur über 0, 1, 2 und 3 laufen würde.
Zweitens ist die gesamte Bedingung innerhalb nicht ganz schlüssig.
Ich habe den Verdacht, dass du hier eher folgendes Beabsichtigst:
Was mich im Moment noch stutzig macht ist die eigenartige If-Bediengung Das macht nicht sonderlich viel Sinn den leeren String hier zu verwenden, jeder String enthält den leeren String. Das ist als ob du " if true " schreiben würdest nur aufwendiger...
...
ich vermute mal, waffe.include?(val) ist Array#include?, laut Dokumentation hat String nichtmal eine include?-Funktion. Und ein leerer String als Element eines Array macht wieder Sinn.
Das die Arrays hier genauso funktionieren, wie in C hatte ich bereits vermutet... leider ist das bei dem komischen Ruby kaum zu erkennen, dass es dort auch klassische Zeiger gibt...
Die Variante mit @skills.dup hat übrigens auch nicht funktioniert. Eigentlich hat gar nichts funktioniert, der Effekt war bei jeder Änderung der selbe. Jetzt hab ich es auf die ganz grobe Art gemacht. (Siehe ganz unten.)
Zitat
Was mich im Moment noch stutzig macht ist die eigenartige If-Bediengung
Code:
if waffe.include?("")
Das macht nicht sonderlich viel Sinn den leeren String hier zu verwenden, jeder String enthält den leeren String. Das ist als ob du " if true " schreiben würdest nur aufwendiger...
...
Das war ein Fehler aus einer alten Variante, den ich leider mitkopiert hatte. Ich hab den kurz, nachdem ich hier gepostet hatte, noch entfernt. Leider warst du da schon am schreiben " Das machte an der Stelle wirklich kein Sinn.
Am Ende steht allerding "if !waffe.include?("")", das macht tatsächlich Sinn. Shisu hat schon Recht. Die !x-Parameter stehen tatsächlich im Namen der Waffe und werden mit einer Funktion aus dem Namen raus genommen und in ein Array gespeichert. Dieses wird dann hier wieder an die Funktion übergeben.
Wenn kein Parameter im Namen steht, dann ist steht in dem Array nur der leere String. Und dann sollen nur alle Fertigkeiten entfernt werden. Passt also schon.
Das mit .. oder ... bei for-Schleifen war mir allerdings neu. Hab mich da schon in anderen Skripts öfter mal gewundert, warum da manchmal von 0 bis .size und manchmal 1 bis .size drin war und beides funktioniert hat. Dass es dann an dem . mehr oder weniger liegt, okay.
Das break war ebenfalls beabsichtigt, weil nur Skills mit einer ID < 25 entfernt werden. Alle anderen bleiben drin.
Wie gesagt, ich hab es jetzt auf die harte Tour gemacht, weil alles andere nicht mehr funktioniert hat und ich auch langsam frustriert war.
Ich sage euch vielen Dank, soweit wieder alles schön.
Schritt 3:
Wird nichtmehr weiter ausgeführt, da die While-Schleife hier bereits beendet worden ist.
Soweit ich dich verstanden habe soll dem aber nicht der Fall sein.
Es würde nämlich sonst nur bis zu dem ersten Vorkommen eines Skills mit einer ID < 25 gesucht werden und danach abgebrochen werden ohne Acht darauf zu geben wie viele weitere Skills noch kommen würden und welche ID diese haben.
Tatsächlich müsste dein Code an der Stelle wie folgt aussehen:
Um alle Skills mit den ID's < 25 heraus zu filtern.
Alternativ könntest du auch zuerst @skills.sort! ausführen und danach iterieren bis du eine ID >= 25 gefunden hast.
Außerdem sollte der Code
Soweit ich deine Absicht damit richtig verstanden habe vielleicht lieber folgendermaßen geschrieben werden:
Es ist definitiv leichter verständlich, ich nehme an auch effektiver von der Performance her aber das ist nicht empirisch von mir überprüft worden sondern lediglich eine Vermutung.
Noch einfacher kann man sich das Leben mit select machen. Select filtert ein Array nach einer bedingung, in deinem Beispiel wäre das:
@skills = @skills.select{|s| s >= 25}
Sollte eigentlich einfach verständlich sein, im Grunde gibst du in den { } select eine Funktion mit, nach der gefiltert werden soll. Mit |s| gibst du an, in welche Variable die einzelnen Werte gespeichert werden sollen, damit du damit irgendwas machen kannst (könnte auch ein beliebiger anderer Bezeichner sein), danach gibst du die Bedingung an. Alle Elemente die die Bedingung erfüllen bleiben drin, der Rest fliegt raus.
Ein Aufruf [1,26,5,30,2].select{|s| s >= 25} würde beispielsweise [26,30] zurückgeben. Dabei ist zu beachten, dass select ein neues Array zurückgibt und das Originalarray unverändert lässt. Ich hoffe, dass war jetzt etwas hilfreich und nicht mehr verwirrend ^^
Es gibt eigentlich recht viele hilfreiche Methoden auf Array von Ruby, es lohnt sich, da mal die Dokumentation (http://www.ruby-doc.org/core-1.9.3/Array.html) durchzulese - Da findet man einige Methoden, die einem das leben oft einfacher machen
Was ich noch etwas merkwürdig finde ist der Gesamteffekt deiner Methode. Du tust neue Werte in das Array, allerdings sind alle möglichen neuen Werte kleiner als 25, und werden dann wieder rausgefiltert? Oder sehe ich das falsch?
leider ist das bei dem komischen Ruby kaum zu erkennen, dass es dort auch klassische Zeiger gibt...
...
Es gibt in Ruby auch keine klassischen Zeiger. Im Prinzip musst du dir das so vorstellen, dass alle Objekte in Ruby nur über "Zeiger" referenziert werden. Call-by-Value gibt es in Ruby nicht (wobei es bei konstanten wie z.B. Integer-Literalen keine Rolle spielt ob sie referenziert oder kopiert werden). Das Zeiger setze ich mal in Anführungszeichen, weil die Ruby-Variablen eigentlich nicht wie richtige Zeiger funktionieren. Du kannst zwar Referenzen auf ein Objekt halten, aber z.B. nicht auf eine Variable (so irrsinnige Sachen wie Pointer auf einen Pointer auf eine Variable, wie in C Gang und Gäbe, gibt es in Ruby nicht).
Wenn du von C kommst, dürfte das Konzept von Ruby erstmal gewöhnungsbedürftig sein. Aber ich denke es sollte klar sein, dass solche Konzepte wie Zeiger bei einer Scriptsprache einfach keinen Sinn machen, da sie viel zu fehleranfällig sind.
Zum Problem: Es wurde ja eigentlich schon alles gesagt. Schleifen (sowohl while als auch for) würde ich in Ruby nicht verwenden. Es gibt stattdessen die Iteratoren, die das viel einfacher und besser können. Statt select kannst du auch delete_if nehmen. Das wirkt in-place (gibt also keine Kopie des Arrays zurück).
Statt mehrfaches aufrufen von push kannst du auch << verwenden (also array << element1 << element2).
Ja, tust du und Cornix in der Hinsicht wohl Das @skills-Array ist eigentlich immer sortiert, bevor meine Funktion aufgerufen wird. Deshalb sind die kleinen Werte am Anfang. Wenn ich dann neue pushe, kommen die ans Ende. Dadurch werden auch doppelte aussortiert, weil im Grunde ja nur die kleinen am Anfang verschwinden, am Ende aber bleiben.
Zugegeben, das ist alles andere als schön, aber solange es funktioniert.
Allerdings werde ich trotzdem mal .select oder .delete_if ausprobieren.
Dank auch an KD, das bringt mein Verständnis von Ruby nochmal ein Stückchen weiter. Scheint so, als wäre das wohl alles mehr Umgewöherei als ich vermutet habe. Aber solange man Hilfe bekommt ist ja alles in Ordnung.
Ja, tust du und Cornix in der Hinsicht wohl Das @skills-Array ist eigentlich immer sortiert, bevor meine Funktion aufgerufen wird. Deshalb sind die kleinen Werte am Anfang. Wenn ich dann neue pushe, kommen die ans Ende. Dadurch werden auch doppelte aussortiert, weil im Grunde ja nur die kleinen am Anfang verschwinden, am Ende aber bleiben.
Zugegeben, das ist alles andere als schön, aber solange es funktioniert.
...
Ah okay, dachte mir dass da etwas mehr dahintersteckt
In dem Fall kommst du mit select oder delete_if auch nicht viel weiter, da diese Methoden dann auch deine neuen Werte, die du gerade gepusht hast, rausfiltern würden. Dann müsstest du select / delete_if aufrufen, bevor du die neuen Werte reinpusht.
Würde also folgenden Code geben:
Potentielles Problem mit deinem Codeschnipsel mit while seh ich dann noch, wenn kein Skill mit einer Nummer größer als 25 drin ist würde er die neuen Werte trotzdem löschen. Also bei [1,25,1] würde [25,1] rauskommen, bei [1,24,2] aber ein leeres Array.
Und mal nebenbei, um sicher zu gehen, dass ich deine Absicht jetzt richtig verstanden habe:
Skills >= 25 sind permanente Skills, Skills < 25 sind waffenabhängig und wenn man die Waffe wechselt, vergisst man alle Skills (<25) der alten Waffe und kann nur noch die Skills der neuen Waffe.
Wird aus deiner Eingangsfrage wirklich nicht ganz klar, deshalb denke ich haben auch viele den Nutzen der Schleife am Ende missverstanden
Geändert von Fatalis (12.12.2011 um 17:41 Uhr)
Grund: Codeschnipsel
Potentielles Problem mit deinem Codeschnipsel mit while seh ich dann noch, wenn kein Skill mit einer Nummer größer als 25 drin ist würde er die neuen Werte trotzdem löschen. Also bei [1,25,1] würde [25,1] rauskommen, bei [1,24,2] aber ein leeres Array.
...
Stimmt eigentlich, aber das Problem war sogar schon unbewusst dadurch gelöst, dass es einen Standard-Skill gibt den jeder Charakter von Anfang an hatte.
Zitat von Fatalis
Und mal nebenbei, um sicher zu gehen, dass ich deine Absicht jetzt richtig verstanden habe:
Skills >= 25 sind permanente Skills, Skills < 25 sind waffenabhängig und wenn man die Waffe wechselt, vergisst man alle Skills (<25) der alten Waffe und kann nur noch die Skills der neuen Waffe.
Wird aus deiner Eingangsfrage wirklich nicht ganz klar, deshalb denke ich haben auch viele den Nutzen der Schleife am Ende missverstanden
...
Bingo
Ich war beim erklären von Problemen noch nie sehr gut, weil mir beim erklären meist potenzielle Lösungen in den Kopf komen, die ich dann entweder direkt ausprobieren oder drüber nachdenken muss. Daher komm ich beim erklären oft durcheinander oder bekomme verkorkste Erklärungen hin.
Aber auf lang oder kurz wusstet ihr ja worum es geht.
Und da ich jetzt auch endlich mal Zeit habe, werde ich es jetzt auch mal umsetzen...
Ich finde dein Konstrukt weder sinnhaft noch sonderlich einfach gestaltet.
Warum speicherst du permanente Skills und Waffenskills nicht in zwei verschiedenen Arrays? Dann musst du dich weniger damit rumplagen wo was steht und welche ID ein Skill hat.