Slobodan: Doppelte Werte aus Listen löschen ...

hi,

in einer Schleife möchte ich aus einer Liste einige
(doppelte) Werte löschen mit der funktion splice.

Nun ... innerhalb eine Schleife, z.B.

-----------------
$array = @array;
for(my $blabla = 0; $blabla < $array; $blabla++ ) {
  ...
  splice @array, ...
  ...
}
----------------

stellt sich ein Problem: Was wird aus dem $array bzw. $_ wenn ein Element mit splice innerhalb der Schleife
entfernt wird?

Anderes gefragt: Wann wird die Funktion splice am @array
sichtbar, und wann wird die eigentlich ausgeführt?

Wenn das in einer Schleife passiert die abhängig von der Anzahl der Elemente ist - wird das ein wenig problematisch. Oder muss ich die Abhängigkeit auf eine andere Variable binden (und diese per Hand reduzieren mit dem "verschwinden" der Elementen) und nicht an denn
$array.

Links aus diesem ( doch speziellem ) Gebiet ?

Danke im Voraus

bye,
slobo


  1. $array = @array;
    for(my $blabla = 0; $blabla < $array; $blabla++ ) {
      ...
      splice @array, ...
      ...
    }

    stellt sich ein Problem: Was wird aus dem $array bzw. $_ wenn ein Element mit splice innerhalb der Schleife
    entfernt wird?
    Anderes gefragt: Wann wird die Funktion splice am @array
    sichtbar, und wann wird die eigentlich ausgeführt?
    Wenn das in einer Schleife passiert die abhängig von der Anzahl der Elemente ist - wird das ein wenig problematisch. Oder muss ich die Abhängigkeit auf eine andere Variable binden (und diese per Hand reduzieren mit dem "verschwinden" der Elementen) und nicht an denn
    $array.
    Links aus diesem ( doch speziellem ) Gebiet ?
    Danke im Voraus
    bye,
    slobo

    ---------------------------------------------

    slice ändert, wie jede änderung an $_ auch, direkt die globale variable.
    deine $array var vorher definiert, also ändert sich nichts daran.
    die länge des arrays ändert sich unmittelbar mit dem entfernen des wertes (siehe abs. 1)
    $_ wird von slice (wahrscheinlich) nicht beeinflusst. du änderst schließlich @array direkt und nicht den wert des funktionsaufrufs.
    eine lösung (wenn auch eine schlechte): wenn ein wert des arrays entfernt wird, ändere manuell $blabla oder $array

    ciao
    F

    1. Hallo Ihr!


      $array = @array;
      for(my $blabla = 0; $blabla < $array; $blabla++ ) {
        ...
        splice @array, ...
        ...
      }

      slice ändert, wie jede änderung an $_ auch, direkt die globale variable.
      deine $array var vorher definiert, also ändert sich nichts daran.
      die länge des arrays ändert sich unmittelbar mit dem entfernen des wertes (siehe abs. 1)
      $_ wird von slice (wahrscheinlich) nicht beeinflusst. du änderst schließlich @array direkt und nicht den wert des funktionsaufrufs.
      eine lösung (wenn auch eine schlechte): wenn ein wert des arrays entfernt wird, ändere manuell $blabla oder $array

      Wie sagte Cheatah doch weiter oben???
      "warum einfach, wenn's auch kompliziert geht? :-)"

      Es ist sicherlich wahr, daß sich das Array sofort verändert, wenn man mit splice drauf los geht. Aber es muß ja nicht gleich zu Problemen führen:
      ----------------
      for(my $blabla = @array; $blabla; $blabla-- ) {
        ...
        splice @array, ...
        ...
      }
      ----------------
      Wenn man also das Array von hinten bearbeitet, bekommt gar nicht mit, daß dieses gelegentlich mitschrumpft (bloß nicht nach hinten schauen ;-)

      Davon abgesehen wirken natürlich die Hash-Varianten wesentlich geschickter, da man die ganzen (in dieser Splice-Variante ausgelassenen) Vergleiche gar nicht mehr hat. Ebenso entfällt bei geschickter Nutzung die for-Schleife ... hauptsache, man sieht dem Programm noch an, was es eigentlich macht! Und wenn man den Kommentar bemühen muß ;-)

      Jörk

  2. hi!

    in einer Schleife möchte ich aus einer Liste einige
    (doppelte) Werte löschen mit der funktion splice.

    Mal abgesehen von deinem Problem: die einfachste Methode, um doppelte Werte aus einem Array zu löschen, dürfte wohl folgende sein:

    for (@array)
    {
      push (@newarray, $_) unless $seen{$_}++;
    }

    oder eine etwas andere Methode:

    for (@array)
    {
      $seen{$_}++;
    }
    @newarray = keys %seen;

    bye, Frank!

  3. Hi,

    in einer Schleife möchte ich aus einer Liste einige
    (doppelte) Werte löschen mit der funktion splice.

    warum einfach, wenn's auch kompliziert geht? :-)

    perldoc perlfaq4 meint dazu:

    "How can I extract just the unique elements of an array?

    There are several possible ways, depending on whether the array is ordered and whether you wish to preserve the ordering.
    [...]
    b) If you don't know whether @in is sorted:

    undef %saw;
             @out = grep(!$saw{$_}++, @in);

    [...]"

    Es empfiehlt sich, ab und zu mal in der ber Perl mitgelieferten und äußerst umfang- sowie nicht minder hilfreichen Dokumentation nachzuschlagen.

    Cheatah

    P.S.: Anders gesagt: Es empfiehlt sich immer, etwas perldoc im Haus zu haben ;-)

    1. hi!

      @out = grep(!$saw{$_}++, @in);

      Über die Möglichkeit bin ich auch gestolpert, erschien mir dann aber doch etwas komplizierter zu verstehen als die beiden anderen Lösungen, die ich gepostet habe, deshalb habe ich es nicht mit aufgenommen.
      Kannst du denn genau erklären, wie diese Methode funktioniert, oder hast du es einfach nur abgeschrieben? ;))

      bye, Frank!

      1. hi!

        @out = grep(!$saw{$_}++, @in);

        Kannst du denn genau erklären, wie diese Methode funktioniert, oder hast du es einfach nur abgeschrieben? ;))

        Wieso sollte er's nicht erklaeren koennen? Steht doch im Klartext in der perlfunc man page. Und auf den Kopf gefallen ist Cheatah ja nun auch nicht.

        <CITE>
        grep EXPR,LIST
        grep BLOCK LIST

        [...]
        Evaluates the BLOCK or EXPR for each element of LIST (locally setting $_ to each element) and returns the list value consisting of those elements for
        which the expression evaluated to TRUE.
        </CITE>

        Naja, das kennste ja sicher selber. Fuer jedes Listenelement wird ein Element im Hash saw mit dem Wert 1 angelegt (denn undef()+1 ist 1). Das Konstrukt gibt aber trotzdem undefined zurueck, da die Inkrementierung erst nach dem Bilden des Return-Wertes durchgefuehrt wird (weil ++ hinten steht und nicht vorn). Also gibt die Hash-Abfrage (ohne !) undefined zurueck, wenn ein Element noch nicht gesichtet wurde, wodurch wegen ! eine 1 entsteht und grep() dieses Listenelement outputtet. Beim naechsten Auftreten desselben Listen-Elements wird das zugehoerige Hash-Element auf 2 erhoeht, nicht jedoch bevor die bereits drinstehende 1 mit ! in false umgewandelt wird und das Listenelement demnach von grep() nicht mehr ausgeworfen wird. Eigentlich ziemlich genial, dieses kleine Stueckchen Code. Aber das ist eben Perl! Da kannste Sachen in einer Zeile machen, fuer die Du woanders ne ganze Seite schreiben musst.

        Calocybe

        1. Fuer jedes Listenelement wird ein Element im Hash saw mit dem Wert 1 angelegt (denn undef()+1 ist 1). Das Konstrukt gibt aber trotzdem undefined zurueck, da die Inkrementierung erst nach dem Bilden des Return-Wertes durchgefuehrt wird (weil ++ hinten steht und nicht vorn). Also gibt die Hash-Abfrage (ohne !) undefined zurueck, wenn ein Element noch nicht gesichtet wurde, wodurch wegen ! eine 1 entsteht und grep() dieses Listenelement outputtet. Beim naechsten Auftreten desselben Listen-Elements wird das zugehoerige Hash-Element auf 2 erhoeht, nicht jedoch bevor die bereits drinstehende 1 mit ! in false umgewandelt wird und das Listenelement demnach von grep() nicht mehr ausgeworfen wird. Eigentlich ziemlich genial, dieses kleine Stueckchen Code. Aber das ist eben Perl! Da kannste Sachen in einer Zeile machen, fuer die Du woanders ne ganze Seite schreiben musst.

          Calocybe

          Auf jeden Fall, habe ich dieses Posting in das Verzeichnis "/perl/wissen_pur" kopiert. ;-)

          bye

          slobo

          1. Hi Slobo!

            Auf jeden Fall, habe ich dieses Posting in das Verzeichnis "/perl/wissen_pur" kopiert. ;-)

            Ich fass' das jetzt einfach mal als Kompliment auf, also Danke! :-)
            Wenn ich das gewusst haette, haette ich mir natuerlich mehr Muehe gegeben. Aber jetzt gibt's erstmal keinen Nachschlag. *faul sei* ;-)

            Calocybe

          2. Hi,

            Auf jeden Fall, habe ich dieses Posting in das Verzeichnis "/perl/wissen_pur" kopiert. ;-)

            das ist sicherlich nicht falsch ;-) aber wenn Du Dich in perldoc ein wenig zurechtfindest und weißt, welche Seiten evtl. das Verhalten ein wenig weiter beschreiben, wirst Du feststellen, daß es sich eigentlich um eine "ganz normale" Interpretation handelt. Um eine sehr gute, wie ich neidlos hinzufügen will :-)

            Es empfiehlt sich aber wirklich, ab und zu mal perldoc aufzurufen und auch einigen Querverweisen zu folgen - oder eben einfach nur mal so zu stöbern. Als kleinen Test kannst Du ja mal herausfinden, was $$ bedeutet - ist manchmal ganz nützlich ;-)

            Also, regelmäßig bei perldoc reinschauen. Die perlfaq gelegentlich mal durchwühlen, perlop/perlvar/etc. lesen, perlfunc zu schätzen lernen. Man lernt viel dabei!

            Cheatah

        2. hi!

          @out = grep(!$saw{$_}++, @in);
          Kannst du denn genau erklären, wie diese Methode funktioniert, oder hast du es einfach
          nur abgeschrieben? ;))
          Wieso sollte er's nicht erklaeren koennen? Steht doch im Klartext in der perlfunc man page.
          Und auf den Kopf gefallen ist Cheatah ja nun auch nicht.

          Das war eigentlich eher ironisch gemeint (daher der Smiley). Wenn man kurz darüber nachdenkt, dann kommt man natürlich mit ein bisschen Perl-Wissen darauf, was da eigentlich passiert. Aber du wirst mir hoffentlich nicht widersprechen, wenn ich behaupte, dass diese Methode nicht die erste ist, die einem in dieser Situation mal eben einfällt :))

          Naja, das kennste ja sicher selber. Fuer jedes Listenelement wird ein Element im Hash saw [...]

          Dem ist wohl nichts mehr hinzuzufügen :)) Doch, eine URL vielleicht noch: http://www.itknowledge.com/tpj/contest.html. Besonders interessant sind dort die "Obfuscated Perl Contests", die Wettbewerbe für konfusestes Perl. Gibt es übrigens auch für C und einige andere Programmiersprachen.

          Die URL, auf der ein dreizeiliger RSA-En-/Decoder in Perl vorgestellt wird, habe ich leider nicht mehr gefunden ;(

          bye, Frank!

          bye, Frank!

    2. b) If you don't know whether @in is sorted:
               undef %saw;
               @out = grep(!$saw{$_}++, @in);
      [...]"

      Oioioi, kann man mit Perl schreckliche Sachen machen.

      Ich hätte jetzt erst mal in Richtung

      "cat datei sort uniq"

      gedacht - *das* verstehe ich wenigstens noch ... und sort gibt es ja auch in perl, uniq nicht?

      (Mit ist klar, daß pipen nicht die performanteste aller Lösungen ist. Aber schöööön ... :-)

      1. Hi,

        undef %saw;
                 @out = grep(!$saw{$_}++, @in);

        Oioioi, kann man mit Perl schreckliche Sachen machen.

        och, dieses Beispiel geht doch noch ;-)

        Ich hätte jetzt erst mal in Richtung

        "cat datei sort uniq"

        gedacht - *das* verstehe ich wenigstens noch ... und sort gibt es ja auch in perl, uniq nicht?

        Nein, nicht daß ich wüßte. Dazu gibt es dann halt Hashes.

        (Mit ist klar, daß pipen nicht die performanteste aller Lösungen ist. Aber schöööön ... :-)

        Leider ist es auch nicht betriebssystemunabhängig... auf einem M$-Server dürfte es schon wieder nicht klappen ;-)

        Cheatah