Ergebnis 1 bis 13 von 13

Thema: Problem beim Umgang mit Array

Hybrid-Darstellung

Vorheriger Beitrag Vorheriger Beitrag   Nächster Beitrag Nächster Beitrag
  1. #1

    Problem beim Umgang mit Array

    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?
    Code:
      def angriffe_setzen(waffe)
        skills_neu = @skills
        if waffe.include?("!M")
    # Machtwaffen      
          skills_neu.push(3)        # Angriff
        elsif waffe.include?("!S")
    # Schusswaffen      
          skills_neu.push(2)        # Angriff
          skills_neu.push(5)        # Gezielter Angriff
        elsif !waffe.include?("")
    # Normale Waffen
          skills_neu.push(1)        # Angriff
          if waffe.include?("!r") or waffe.include?("!m")
            skills_neu.push(4)      # Gezielter Angriff
          end
          if waffe.include?("!m")
            skills_neu.push(24)      # Meucheln
          end
        end  
        for i in 0..@skills.size
          if @skills[i] < 25
            skills_neu.delete_at(i)
          else
            break
          end
        end
        @skills = skills_neu
        @skills.sort!
      end
    Außerdem bin ich für Optimierungsvorschläge dankbar "

    Geändert von Rettan (11.12.2011 um 21:23 Uhr)

  2. #2
    Arrays werden nicht mit dem Call-By-Value sondern mit dem Call-By-Reference Prinzip übergeben.
    Wenn du schreibst:
    Code:
    skills_neu = @skills
    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
    Code:
    skills_neu = @skills
    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
    Code:
    skills_neu = @skills.dup
    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
    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...

    Was ich ebenfalls nicht ganz verstehe ist folgender Part:
    Code:
    for i in 0..@skills.size
          if @skills[i] < 25
            skills_neu.delete_at(i)
          else
            break
          end
        end
    Erstens muss die Signatur der For-Schleife eher so aussehen:
    Code:
    for i in 0...@skills.size
    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:
    Code:
    for i in 0...@skills.size
           if @skills[i] < 25
             skills_neu.delete_at(i)
           else
             next
           end
         end

    Geändert von Cornix (11.12.2011 um 21:36 Uhr)

  3. #3
    Zitat Zitat von Cornix Beitrag anzeigen
    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...
    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.

  4. #4
    String besitzt eine include?-Methode.
    Waffe wird wohl einfach auf $data_weapons[@weapon_id].name verweisen oder so.

  5. #5
    Okay, erstmal vielen Dank.

    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 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.

    Code:
      def angriffe_setzen(waffe)
        if waffe.include?("!M")
    # Machtwaffen      
          @skills.push(3)        # Angriff
        elsif waffe.include?("!S")
    # Schusswaffen      
          @skills.push(2)        # Angriff
          @skills.push(5)        # Gezielter Angriff
        elsif !waffe.include?("")
    # Normale Waffen
          @skills.push(1)        # Angriff
          if waffe.include?("!r") or waffe.include?("!m")
            @skills.push(4)      # Gezielter Angriff
          end
          if waffe.include?("!m")
            @skills.push(24)      # Meucheln
          end
        end  
        i = 0
        while i < @skills.size
          if @skills[i] < 25
            @skills.delete_at(i)
            i -= 1
          else
            break
          end
          i += 1
        end
        @skills.sort!
      end
    Ich sage euch vielen Dank, soweit wieder alles schön.

    Geändert von Rettan (12.12.2011 um 12:31 Uhr)

  6. #6
    Dann ist das Break keinesfalls richtig.

    Hier ein Beispiel mit deinem Code:
    @skills[0] = 4
    @skills[1] = 27
    @skills[2] = 9

    Was würde nach der Ausführung von diesem Code-Abschnitt passieren:
    Code:
    i = 0     
    while i < @skills.size
          if @skills[i] < 25
            @skills.delete_at(i)
            i -= 1
          else
            break
          end
          i += 1
        end
    Ganz einfach:

    Schritt 1:
    i = 0
    @skills[0] == 4 < 25 => true
    -> @skills.delete_at(0)


    Schritt 2:
    i = 1
    @skills[1] == 27 < 25 => false
    -> break

    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:
    Code:
    for i in 0...@skills.size
      if @skills[i] < 25
        @skills.delete_at(i)
      end
    end
    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
    Code:
    elsif !waffe.include?("")
    Soweit ich deine Absicht damit richtig verstanden habe vielleicht lieber folgendermaßen geschrieben werden:
    Code:
    elsif not waffe.empty?
    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.

    Geändert von Cornix (12.12.2011 um 12:45 Uhr)

  7. #7
    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?

  8. #8
    Zitat Zitat
    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).
    Code:
    @skills.delete_if {|id| id < 25}
    Statt mehrfaches aufrufen von push kannst du auch << verwenden (also array << element1 << element2).

  9. #9
    Zitat Zitat von Fatalis Beitrag anzeigen
    Oder sehe ich das falsch?
    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.

    Danke euch allen.

  10. #10
    Zitat Zitat von Rettan Beitrag anzeigen
    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:

    Code:
     def angriffe_setzen(waffe)
       @skills.delete_if{|s| s<25}
    
        if waffe.include?("!M")  
          @skills << 3
     ....
    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

  11. #11
    Zitat Zitat von Fatalis Beitrag anzeigen
    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 Zitat von Fatalis Beitrag anzeigen
    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...

  12. #12
    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.

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •