Vergleich zweier gleicher Arrays gibt false zurück
Jeena Paradies
- javascript
Hallo,
warum gibt in JavaScript [1] == [1] false aber 1 == 1 true aus?
Jeena
Hello,
warum gibt in JavaScript [1] == [1] false aber 1 == 1 true aus?
ich rate: Arrays werden in JavaScript, ähnlich wie in Java, per Default nicht über den Inhalt sondern die Referenz verglichen.
MfG
Rouven
Hallo Jeena,
warum gibt in JavaScript [1] == [1] false aber 1 == 1 true aus?
== vergleicht bei Objekten (Arrays sind in JS Objekte) nur, ob die Variablen eine Referenz auf's gleiche Objekt sind, nicht jedoch auf Gleichheit des Objektinhalts.
Siehe z.B.: http://forum.de.selfhtml.org/archiv/2006/11/t140356/
Viele Grüße,
Christian
Hallo,
== vergleicht bei Objekten (Arrays sind in JS Objekte) nur, ob die Variablen eine Referenz auf's gleiche Objekt sind, nicht jedoch auf Gleichheit des Objektinhalts.
Genau. Und zwar gilt das für alle Objekte im Gegensatz zu sogenannten primitive values. Kein Objekt ist mit einem anderen gleich, es sei denn, es ist mit ihm identisch. Gleich sind nur Primitives (daher 1 == 1), Objektes haben keine Gleichheit, sondern nur Identität (eine »Hausnummer«, das Verweisen auf eine bestimmte Speicherstelle, wie du sagst).
Da habe ich schon öfters etwas zu geschrieben:
</archiv/2006/12/t141628/#m920393>
</archiv/2005/9/t114730/#m732136>
</archiv/2007/4/t151203/#m983410>
Mathias
Hallo,
== vergleicht bei Objekten (Arrays sind in JS Objekte) nur, ob die Variablen eine Referenz auf's gleiche Objekt sind, nicht jedoch auf Gleichheit des Objektinhalts.
Ok, danke für die Erklärung. Irgendwie kann ich das aber nicht so richtig gut finden. Wenn man so einen Vergleich macht dann will man doch eigentlich fast immer dass da true rauskommt.
Jeena
Hallo Jeena!
Wenn man so einen Vergleich macht dann will man doch eigentlich fast immer dass da true rauskommt.
Mit meinem frührern »Verständnis« der Programmierung hätte ich genauso reagiert wie Du. Heute bin ich 2cm weiter, und es erscheint mich logisch (hier mit Perl):
my $a = [1];
my $b = [1];
my $x = 1;
my $y = 1;
# $/ entspricht defaultmässig \n, wenn nicht anders definiert...
print '$a: ', $a, $/;
print '$b: ', $b, $/;
print '$x: ', $x, $/;
print '$y: ', $y, $/;
print 'VERGLEICH $a <-> $b:', $/;
if ($a == $b) { print "TRUE\n"; } else { print "FALSE\n"; }
print 'VERGLEICH $x <-> $y:', $/;
if ($x == $y) { print "TRUE\n"; } else { print "FALSE\n"; }
Ausgabe:
$a: ARRAY(0x35218) <- Na?
$b: ARRAY(0x35434) <- Klingelt's?
$x: 1
$y: 1
VERGLEICH $a <-> $b:
FALSE
VERGLEICH $x <-> $y:
TRUE
Viele Grüße aus Frankfurt/Main,
Patrick
Hallo,
$a: ARRAY(0x35218) <- Na?
$b: ARRAY(0x35434) <- Klingelt's?
Mir ist schon klar dass das nicht das selbe Array ist, aber es die beiden sind doch gleich.
$a = [1]
$b = [1]
If $a Equals $b
Then TRUE
Else FALSE
=> TRUE
$a = [1]
$b = $a // Referenz nicht Kopie
If $a is $b
Then TRUE
Else FALSE
=> TRUE
$a = [1]
$b = $a.clone() // Kopie
If $a is $b
Then TRUE
Else FALSE
=> FALSE
So weit ich des Englischen mächtig bin heißt = "equals"
Jeena
So weit ich des Englischen mächtig bin heißt = "equals"
eben, du brauchst eine equals Funktion. leider hast du nicht gesagt was für eine Sprache das ist (für mich sieht's nach VB aus), aber offensichtlich gibt es dort eine equals Funktion für Arrays, die die Elemente vergleicht. In JS (und vielen anderen Sprachen) musst du dir diese selbst bauen (s. mein anderes Posting)
Struppi.
Hallo,
So weit ich des Englischen mächtig bin heißt = "equals"
eben, du brauchst eine equals Funktion. leider hast du nicht gesagt was für eine Sprache das ist (für mich sieht's nach VB aus), aber offensichtlich gibt es dort eine equals Funktion für Arrays, die die Elemente vergleicht. In JS (und vielen anderen Sprachen) musst du dir diese selbst bauen (s. mein anderes Posting)
Oh hehe, ich glaube da haben wir uns missverstanden eigentlich wollte ich schreiben:
'So weit ich des Englischen mächtig bin heißt das Zeichen "=" "equals" in Englisch.'
Das war nur pseudocode und eigentlich wollte ich damit den unterschied zwischen "is" und "equals" verdeutlichen. Ich verstehe auch dass es so in JavaScript (im gegenteil zu Java, wo wie du schreibst "==" da true ausgibt) ist, ich wundere mich nur warum man sich dafür entschieden hat das so zu spezifizieren, da ich der meinung bin dass man ja meistens will dass da true herauskommt und für die wenigen ausnahmen, da wäre es dann sinnvoller eine .is() Methode zu haben, wie das wohl bei Java (auf jeden fall aber z.B. in Ruby) ist.
Jeena
Hallo,
ich wundere mich nur warum man sich dafür entschieden hat das so zu spezifizieren
Weil es eine ganz allgemeine Regel ist, die alle Objects betrifft.
Es besteht kein Problem, wenn ein Arrays nur Primitives enthält. Das heißt, [1, 2, 3] == [1, 2, 3] ist natürlich trivial zu vergleichen.
Wenn das nicht der Fall ist, läuft der Wunsch, zwei Arrays zu vergleichen, auf einen allgemeinen Algorithmus hinaus, der die Gleichheit von Objekten prüft. Zum Beispiel beim Vergleich [window] == [window], der auf window == window hinausläuft, wäre ein definiertes Konzept von Gleichheit nötig, wenn == nicht bloß die Identität der Objekte prüfen soll.
Sinnvollerweise und unsinnigerweise sähe dieses Konzept so aus, dass der Interpreter alle Member durchgeht und vergleicht. Wenn es sich um Objekte handelt, müsste er wieder dessen Member vergleichen, rekursiv halt. Das wäre bei window == window reiner Overkill.
Will man das vermeiden, müsste man jedem Objekttyp seine eigene equals-Methode geben, die den Operator == überlädt. Die könnte dann einfacher und spezifischer sein. Dann hat man aber keine allgemeine Operanden-unabhängige Bedeutung von ==, man könnte die Resultate nicht mit ein paar Regeln abschätzen.
Mathias
Hallo Jeena!
nicht das selbe Array ist, aber es die beiden sind doch gleich.
Sie enthalten Elemente mit dem gleichen Wert, ja. Aber in Vorahnung einer ähnlichen Frage hatte ich gestern abend das gepostete Beispiel fortgeführt ;)
my $a = [1];
my $b = [1];
my $x = 1;
my $y = 1;
# $/ entspricht defaultmässig \n, wenn nicht anders definiert...
print '$a: ', $a, $/;
print '$b: ', $b, $/;
print '$x: ', $x, $/;
print '$y: ', $y, $/;
print 'VERGLEICH $a <-> $b:', $/;
if ($a == $b) { print "TRUE\n"; } else { print "FALSE\n"; }
print 'VERGLEICH $x <-> $y:', $/;
if ($x == $y) { print "TRUE\n"; } else { print "FALSE\n"; }
print 'Jetzt dereferenzieren wir... und vergleichen somit den _Inhalt_:', $/;
if ($a->[0] == $b->[0]) { print "TRUE\n"; } else { print "FALSE\n"; }
print 'Jetzt stiften wir Verwirrung...:', $/;
print \$x, $/;
print \$y, $/;
print 'OBJEKT-VERGLEICH FUER SKALARE $x <-> $y: ', $/;
if (\$x == \$y) { print "TRUE\n"; } else { print "FALSE\n"; }
Ausgabe:
$a: ARRAY(0x35200)
$b: ARRAY(0x3541c)
$x: 1
$y: 1
VERGLEICH $a <-> $b:
FALSE
VERGLEICH $x <-> $y:
TRUE
Jetzt dereferenzieren wir... und vergleichen somit den _Inhalt_:
TRUE
Jetzt stiften wir Verwirrung...:
SCALAR(0x18311a0)
SCALAR(0x18311c4)
OBJEKT-VERGLEICH FUER SKALARE $x <-> $y:
FALSE
Viele Grüße aus Frankfurt/Main,
Patrick
Mit meinem frührern »Verständnis« der Programmierung hätte ich genauso reagiert wie Du. Heute bin ich 2cm weiter, und es erscheint mich logisch (hier mit Perl):
Und wer nochmal 2cm weiter ist, dem erscheint das hier logisch (hier mit Python):
* [1, 2, 3] == [1, 2, 3]
True
Und nun?
Hallo,
Und nun?
Andere Sprachen, andere Konventionen. In PHP hast Du das gleiche Verhalten, wie in Python. Und in Perl auch, wenn Du nicht mit Referenzen auf Listen, sondern mit Listen selbst arbeitest (siehe Patricks anderes Posting). So what?
Viele Grüße,
Christian
Hallo,
*
[1, 2, 3] == [1, 2, 3]
Was würde Python bei einem Vergleich zweier komplexerer Objekte machen, also wie in JavaScript das besagte window == window?
Mathias
Hallo Mathias,
*
[1, 2, 3] == [1, 2, 3]
Was würde Python bei einem Vergleich zweier komplexerer Objekte machen, also wie in JavaScript das besagte window == window?
Kommt darauf an[tm].
Wenn die Klasse spezielle Methoden (__eq__, __cmp__) implementiert, dann werden diese Methoden aufgerufen, um Vergleiche durchzuführen. Wenn nicht, dann wird nur geprüft, ob die Variablen Referenzen auf das gleiche Objekt sind.
Beispiel:
Klasse mit Vergleichsfunktion:
class Bar:
def __init__ (self, a):
self.a = a
def __eq__ (self, other):
return self.a == other.a
Klasse ohne Vergleichsfunktion:
class Foo:
def __init__ (self, a):
self.a = a
Ausgabe (Beispiel):
t1 = Foo (1)
t2 = t1
t3 = Foo (1)
t1 == t2
True
t1 == t3
False
s1 = Bar(1)
s2 = s1
s3 = Bar(1)
s1 == s2
True
s1 == s3
True
Viele Grüße,
Christian
Hi Christian
Und wer nochmal 2cm weiter ist, dem erscheint das hier logisch (hier mit Python):
*[1, 2, 3] == [1, 2, 3]
TrueUnd nun?
und nun sagst du mir wie Python die identität zweier listenvariablen überprüfe?
sprich in Perl
\@a == \@b
(habs jetzt in der Guido-dokiu nicht gefunden...)
Grüße
LanX
Hi Christian
vielleicht beser "Hi Timo" :)
Hallo!
und nun sagst du mir wie Python die identität zweier listenvariablen überprüfe?
sprich in Perl
\@a == \@b
[1,2,3] is [1,2,3]
(ergibt: False)
Viele Grüße,
Christian
== vergleicht bei Objekten (Arrays sind in JS Objekte) nur, ob die Variablen eine Referenz auf's gleiche Objekt sind, nicht jedoch auf Gleichheit des Objektinhalts.
Ok, danke für die Erklärung. Irgendwie kann ich das aber nicht so richtig gut finden. Wenn man so einen Vergleich macht dann will man doch eigentlich fast immer dass da true rauskommt.
Tut es ja auch, wenn es die gleichen Objekte sind.
Wenn du wissen willst ob bestimmte Eigenschaften (z.b. der Array inhalt) gleich sind, wird z.b. in Java der Gleichheitsoperator überladen mit eine equals() Funktion, in Javascript leider nicht.
In deinem Fall müßte es so aussehen:
Array.prototype.equals = function(p) {
if(this.length != p.length) return false;
for(var i = 0; i < this.length; i ++) if( this[i] != p[i]) return false;
return true;
}
alert( [1].equals([1]));
Struppi.
HI
In deinem Fall müßte es so aussehen:
Array.prototype.equals = function(p) {
if(this.length != p.length) return false;
for(var i = 0; i < this.length; i ++) if( this[i] != p[i]) return false;
return true;
}
alert( [1].equals([1]));
Überlädt equals jetzt den == operator, oder definierst du hier ne neue Methode equals? Konnte dazu jetzt nichts googlen.
Davon ist nämlich abhängig ob Unterarrays dann mit this[i] != p[i] auch rekursiv untersucht werden, ansonsten müsste es sowas wie
! this[i].equals(p[i])
heißen.
Anders: Ohne overloading würde deine methode in Perl nur ein @this=@p realisieren.
Außerdem würde ich als erstes noch den Fall abfangen, das beide verglichenen Arrays identische Objekte sind und dafür sofort true zurückliefern. Spart rekursionen.
Desweiteren müsste noch diskutiert werden inwieweit sich Ringstrukturen auswirken, da müsste eine Rekursion rechtzeitig abgebrochen werden sonst gibts ne böse Endlosschleife,z.B.
[code lang=javascript]
a=b=Array();
a=[b,...];
b=[a,...];
[code lang=javascript]
Bye
KurtZ
Nachtrag:
Desweiteren müsste noch diskutiert werden inwieweit sich Ringstrukturen auswirken, da müsste eine Rekursion rechtzeitig abgebrochen werden sonst gibts ne böse Endlosschleife,z.B.
a=b=Array();
a=[b,...];
b=[a,...];
was jetzt auch erklären dürfte warum man bei nicht primitiven Datenstrukturen die Ringstrukturen erlauben erstmal nur auf den Vergleich der Referenzen abhebt.
Die Frage ist wie das hier so gelobte Python mit Ringstrukturen umgeht.
also sowas wie
~~~python
b=[]
a=[b,1]
b=[a,2]
d=[]
c=[d,1]
d=[c,2]
a==c
ahh super Pustekuchen, ich kann gar keine Ringstruktur bauen ...
>>> a
[[], 1]
Na wen das nicht besser geht, dann kann ich doch gleich bei Perllisten bleiben.
Hallo,
Überlädt equals jetzt den == operator, oder definierst du hier ne neue Methode equals?
Letzteres natürlich. Es gibt kein Operatoren-Overloading in ECMAScript Edition 3.
Davon ist nämlich abhängig ob Unterarrays dann mit this[i] != p[i] auch rekursiv untersucht werden
Werden sie nicht.
Das ist die Aussage dieses Thread: Man kann Arrays einfach vergleichen, solange sie nur Primitives enthalten. Das war es aber auch.
ansonsten müsste es sowas wie
! this[i].equals(p[i])
heißen.
Okay, prinzipiell kann man auch Arrays rekursiv miteinander vergleichen, solange die Arrays nur Primitives bzw. weitere Arrays bestehend aus Primitives enthalten. Dazu macht man this[i].equals(p[i]), wenn beide Operanden Arrays sind, ansonsten halt this[i] != p[i]. Das muss man aber manuell prüfen, da gibts kein Overloading.
Mathias
Hi
Okay, prinzipiell kann man auch Arrays rekursiv miteinander vergleichen, solange die Arrays nur Primitives bzw. weitere Arrays bestehend aus Primitives
enthalten.
Dazu macht man this[i].equals(p[i]), wenn beide Operanden Arrays sind, ansonsten halt this[i] != p[i]. Das muss man aber manuell prüfen, da gibts kein Overloading.
M.E. geht das eben nicht wirklich, weil man mit Referenzen Ringstrukturen bauen kann, d.h. die Rekursion würde nie abbrechen. Eine allgemeingültige rekursive Realisierung als Methode des Objects Array halte ich nur für sehr schwierig zu realisieren.
Sprachen die eine rekursive Überprüfung erlauben, wie z.B. Python erlauben scheinbar auch nicht Referenzen in Arrays einzutragen (Verschachtelungen erfolgen AFAIS bei Value) und machen so Rückbezüglichkeiten unmöglich.
M.a.W. man kann vernünftigerweise == nicht anders in JS definieren, weil die Arrays mehr erlauben als in Python.(wie es in Java realisiert ist wäre auch interessant zu erfahren)
Und wäre overloading von == in JS erlaubt, hätte man schnell eine rekursive Endlosschleife gebaut...
Sowas wie einen rekursiven Vergleich kann ich mir deswegen nur als komplexe Methode vorstellen, die sich Rückbezüge merkt und den Suchbaum dort abschneidet. Am besten als Klassenmethode Array.compare(arr1,arr2)
Gruß
Kurt
Hi
Sprachen die eine rekursive Überprüfung erlauben, wie z.B. Python erlauben scheinbar auch nicht Referenzen in Arrays einzutragen (Verschachtelungen erfolgen AFAIS bei Value) und machen so Rückbezüglichkeiten unmöglich.
Bin mittlerweile schlauer, diese Sorte Arrays in Python müssen mit "Tupeln" [] und nicht mit "Listen" () realisiert werden, um Rückbezüge zu ermöglichen:
>>> A=['A']
>>> B=['B',A]
>>> A.append(B)
>>> a=['A']
>>> b=['B',a]
>>> a.append(b)
>>> A
['A', ['B', [...]]]
>>> a
['A', ['B', [...]]]
>>> B
['B', ['A', [...]]]
Der rekursive Vergleich klappt dann tatsächlich auch!
>>> A == a
True
Das ist scheints mir sehr gut umgesetzt!!!
Grüße
Kurt
Hallo Kurt,
Bin mittlerweile schlauer, diese Sorte Arrays in Python müssen mit "Tupeln" [] und nicht mit "Listen" () realisiert werden, um Rückbezüge zu ermöglichen:
Du hast da was falsch verstanden: Tupel konstruiert man mit normalen Klammern - (1, 2) - Listen mit eckigen Klammern - [1, 2, 3] - anders als von Dir gesagt. Du nutzt also Listen.
Wo ist der Unterschied? Listen sind nun mal Listen, lang und veränderbar. Tupel dagegen sind fix, man kann sie nicht verändern. Idealerweise stellst Du Dir Tupel nicht als „nicht veränderbare Listen“ vor, sondern gehst mehr der mathematischen Sichtweise aus: Tupel sind dort aufzählbare Strukturen von Objekten, die aus Einzeldaten bestehen. Denk zum Beispiel an Positionen in einem zweidimensionalen Koordinatensystem (X- und Y-Wert) oder allgemeiner an Vektoren. Oder eben an alle möglichen Datensätze, ein Geburtsdatum wäre z.B. als ein Tupel aus Jahr, Monat und Tag (die Einzelwerte sind alles Zahlen) interpretierbar.
D.h. Tupel sind eher ein struktureller Datentyp für all die Fälle, für die man nicht unbedingt ein extra Objekt mit extra Klasse und Methoden und allem Pipapo erschaffen will, um das rumzureichen, sondern für all die kleinen Fälle. Und weil das so praktisch ist, ist das recht gut in die Sprache integriert. Links von der Zuweisung kann man z.B. Tuple Unpacking betreiben:
~~~python-shell
position = (5, 3)
>>> position
(5, 3)
>>> x, y = position
>>> x
5
>>> y
3
D.h. eigentlich wird das Tupel syntaktisch durch die Kommata bestimmt, nur zur syntaktischen Klarheit werden die Klammern drum gemacht:
~~~python-shell
>>> x, y
(5, 3)
Genau das ist auch der Grund, weswegen das Rückgeben mehrerer Einzel-Werte aus Funktionen klappt. Die Funktion divmod() aus den Builtins von Python ist ja so definiert:
~~~python
def divmod(x, y):
return x // y, x % y
Effektiv wird da ein Tupel zurückgegeben; das Paar aus Divisor und Rest:
~~~python-shell
>>> divisor, remainder = divmod(11, 3)
>>> print divisor, "Rest:", remainder
3 Rest: 2
>>> t = divmod(11, 3)
>>> type(t)
<type 'tuple'>
>>> print t
(3, 2)
Tupel haben noch andere Vorteile – die Unveränderbarkeit hat z.B. den praktischen Effekt, dass man sie als Key in einem Dictionary nehmen kann – aber in der Suche auf Deine Ringstrukturen bist Du da auf dem Holzweg.
...
Denn die sind wunderbar mit Listen möglich, wie Du es ja auch vorgeführt hast. Nur hier hast Du einen Fehler begangen, der Dich da auf den Weg brachte, es sei nicht möglich.
~~~python-shell
A = [1, 2]
>>> B = [0, A]
>>> B
[0, [1, 2]]
Anhand der ID – ganz einfach der Speicheradresse - kann man überprüfen, dass die Variable A und das zweite Element von B wirklich auf das gleiche Objekt zeigen; es referieren:
~~~python-shell
>>> id(A)
420800
>>> id(B[1])
420800B
>>> A is B[1]
True
>>> A in B
True
Und jetzt kommt das, was Du in Deinem Posting von Donnerstag falsch gemacht hast:
>>> A = [4, 5]
Du änderst das Objekt, auf das A zeigt hier nämlich NICHT. Du erstellst ein NEUES Listen-Objekt – und das ist logischerweise ein anderes. Das vorherige Objekt wurde aber noch nicht von der Garbage Collection gefressen; schließlich existiert eine Referenz an zweiter Stelle in der Liste B darauf:
~~~python-shell
id(A)
419120
>>> id(B[1])
420800
>>> A is B[1]
False
Also schnell wieder rückgängig machen und A wieder die passende Referenz auf das richtige Objekt verpassen:
~~~python-shell
>>> A = B[1]
>>> id(A)
420800
Die Ringstruktur kriegst Du hin, indem Du logischerweise das von A referenzierte Objekt selber änderst; mit den zugehörigen Methoden und Zuweisungen:
~~~python-shell
A[1] = B
>>> id(A)
420800
>>> id(B)
420760
>>> id(A[1])
420760
>>> id(B[1])
420800
Also nix weiter als ein kleiner Verständnisfehler.
Tim
Hi Tim
Du hast da was falsch verstanden: Tupel konstruiert man mit normalen Klammern - (1, 2) - Listen mit eckigen Klammern - [1, 2, 3] - anders als von Dir gesagt. Du nutzt also Listen.
ja danke ... der Dreher ist mir auch aufgefallen, ich nutze halt vorwiegend Perl und da entsprechen Python-Listen eher Perl-Arrays und Python-Tupel eher Perl-Listen (die es nur Literal gibt ansonsten werden sie in Arrays konvertiert).
Aber ich wollte jetzt nicht die dritte Antwort auf mich selbst posten, zu monologisch ... :)
Denn die sind wunderbar mit Listen möglich, wie Du es ja auch vorgeführt hast. Nur hier hast Du einen Fehler begangen, der Dich da auf den Weg brachte, es sei nicht möglich.
ja den gleichen Fehler hab ich auch im Javascript Beispiel gemacht, interessanterweise ist der Syntax in allen drei Sprachen gleich nur die Terminonolgie ändert sich
z.B.
=[0,1,[20,21]]
gibt mir die Referenz auf ein 3 elemntiges Array mit einem 2 elementigen Subarray am Ende.
Summa summarum muss man anmerken das man in allen Sprachen zwischen dem "selben" und dem "gleichen" Objekt/Datentyp unterscheiden muss und dafür einen jeweils eigenen Syntax braucht. (in Python mit den Operatoren "==" und "is" )
Ciao
Kurt
Hallo Kurt,
ja danke ... der Dreher ist mir auch aufgefallen, ich nutze halt vorwiegend Perl und da entsprechen Python-Listen eher Perl-Arrays und Python-Tupel eher Perl-Listen (die es nur Literal gibt ansonsten werden sie in Arrays konvertiert).
Mein Perl ist brüchig bis kaum vorhanden, aber wenn Du mit Perl-Listen die Syntax ($a, $b, $c) meinst und Perl-Arrays die Syntax [$a, $b, $c], dann .. sind pythonische Tupel immer noch nicht wirklich Listen im Sinne von Perl, so dass Interpolation statt findet. Im Tupel ist dann immer noch eine Referenz gespeichert:
>>> s = 1
>>> t = (s, 2)
>>> id(s)
16790920
>>> id(t)
420640
>>> id(t[0])
16790920
Variablen, Sequenzen, Objekt-Attribute in Python sind doch immer Referenzen.
Tim
Hi Tim
Mein Perl ist brüchig bis kaum vorhanden, aber wenn Du mit Perl-Listen die Syntax ($a, $b, $c) meinst und Perl-Arrays die Syntax [$a, $b, $c], dann .. sind pythonische Tupel immer noch nicht wirklich Listen im Sinne von Perl, so dass Interpolation statt findet. Im Tupel ist dann immer noch eine Referenz gespeichert:
schon klar, ich schrieb bewußt "eher" weil Perl-Listen halt nicht veränderbare Arrays sind, um mit deinen Worten zu sprechen:
Idealerweise stellst Du Dir Tupel nicht als „nicht veränderbare Listen“
Es gibt aber keine Listenvariablen, man kann Listen einer Arrayvariable zuweisen, aber dann wird daraus ein Array. Wie das Arrray dann wideregegeben wird richtet sich nach Kontext und Sigil.
Default, dh kürzeste Schreibweise, ist dabei die Listenform mit dem @, das führt aber leider bei vielen Leuten zu dem Missverständnis Listen und Arrays wären in Perl Synonyme, obwohl es feine Unterschiede gibt.
>>> s = 1
>>> t = (s, 2)
>>> id(s)
16790920
>>> id(t)
420640
>>> id(t[0])
16790920
analog im Perldebugger ( perl -de0)
DB<1> $s=1;
DB<2> @t=(\$s,2); # weise Array t die Liste zu
DB<3> p \$s; #referenz zu $s
SCALAR(0x83c5d78)
DB<4> p \@t; #referenz zu @t
ARRAY(0x83b7784)
DB<5> p $t[0];
SCALAR(0x83c5d78)
In Perl kommt man aber sehr selten auf die Idee die Referenz eines Skalars wie $s abzulegen. Das braucht man IMHO so gut wie nie.
Die Möglichkeiten sind die selben, die Defaults sind halt andere, in perl muss man explizit refrenzieren um eine Refrenz abzulegen und diese später wieder explizit derefrenzieren.
Braucht man Referenzen, kann man die Listenform umgehen und konsistent nur mit Referenzen arbeiten, der Syntax wird aber aufwändiger, weils (leider) nicht default ist.
DB<6> $$s_ref=1
DB<7> $t_ref=[$s_ref,2]
#oder: @$t_ref=($s_ref,2)
DB<8> print $s_ref
SCALAR(0x83c5e20)
DB<9> p $t_ref
ARRAY(0x83c5e14)
DB<10> p $$t_ref[0]
#oder: p $t_ref->[0]
SCALAR(0x83c5e20)
... und Perl-Listen kann man nicht schachteln so wie Python-Tupel, sie werden immer abgeflacht. Will man Schachteln muss man Arrayrefrenzen ablegen.
DB<18> @A=(1,2,3)
DB<19> @B=(4,5,6,@A)
DB<20> p @B
456123
Aber auch die Rückgabe mehrerer Werte funktuioniert analog zu Python-Tupel über Perl-Listen
DB<21> sub func {return 1,2,3}
DB<22> ($a,$b,$c)=func()
DB<23> p $a,$b,$c
123
Alles nur der Vollständigkeit halber, ich denke du kennst Perl gut genug! :)
Aber ich denke für die anderen wird klar, das Python-Tupel wohl am ehesten als schachtelbare und ablegbare Perl-Listen beschrieben werden können.
Ciao
Kurt
Hi
>>> s = 1
>>> t = (s, 2)
>>> id(s)
16790920
>>> id(t)
420640
>>> id(t[0])
16790920
also, eine Sache verstehe ich hier nicht....
dieses Verhalten das t[0] auf die selbe Variable wie s zu referenzieren,
würde doch bedeuten (und Sinn machen), dass es sich um Aliase handelt, d.h. eine Änderung von des Inhalts von s würde auch t[0] ändern und vice versa.
mit s=2 ändere ich aber auch die id(s) und t[0] behält den alten wert...?!?
habe ich was verpasst?
Ciao
Kurt
Hallo Kurt,
dieses Verhalten das t[0] auf die selbe Variable wie s zu referenzieren,
würde doch bedeuten (und Sinn machen), dass es sich um Aliase handelt, d.h. eine Änderung von des Inhalts von s würde auch t[0] ändern und vice versa. mit s=2 ändere ich aber auch die id(s) und t[0] behält den alten wert...?!?
Wenn Du s auf ein neues Int-Objekt mit dem Wert 2 zeigen lässt, dann hat s eben eine neue Referenz auf ein anderes Objekt. t[0] zeigt weiterhin stabil und unveränderbar auf das originale Objekt; hat deswegen noch immer die gleiche ID:
>>> s = 1
>>> id(s)
16790920
>>> t = (s, 2)
>>> id(t[0])
16790920
>>> s = 2
>>> id(s)
16790908
>>> id(t[0])
16790920
Zahlen (und Strings .. und Tupel) sind in Python unveränderbar, jede Operation auf diese hat eine neue, keine geänderte Zahl und damit ein neues, anderes und kein geändertes Objekt zur Folge. Lustiger wird es, wenn man einen veränderbaren Objekt-Typ nimmt:
>>> li = [1, 2]
>>> id(li)
420480
>>> t = (li, "foo")
>>> t
([1, 2], 'foo')
>>> id(t[0])
420480
>>> li[0] = "bar"
>>> li
['bar', 2]
>>> id(li)
420480
>>> t
(['bar', 2], 'foo')
>>> id(t[0])
420480
Die Unveränderbarkeit des Tupels bezieht sich also auf die darin enthaltenen Referenzen, nicht auf die gesamte referenzierte Struktur selbst.
Tim
Hi
Zahlen (und Strings .. und Tupel) sind in Python unveränderbar, jede Operation auf diese hat eine neue, keine geänderte Zahl und damit ein neues, anderes und kein geändertes Objekt zur Folge.
was hilft mir dann die Info welche Objekt-ID eine Zahl hat wenn das Objekt unveränderbar ist?
Grüße
kurt
Hallo,
was hilft mir dann die Info welche Objekt-ID eine Zahl hat wenn das Objekt unveränderbar ist?
Es sollte Dir nur sagen, dass die Referenz im Tupel immer noch vorhanden ist.
Tim
Hallo,
warum gibt in JavaScript [1] == [1] false aber 1 == 1 true aus?
Zur Ergänzung:
ECMAScript: The Abstract Equality Comparison Algorithm
Das läuft bei zwei Arrays darauf hinaus:
»Return true if x and y refer to the same object or if they refer to objects joined to each other (see 13.1.2). Otherwise, return false.«
(Joint objects habe ich noch nie gesehen. Das gibts wohl in der Praxis nicht.)
Mathias
gruss Jeena,
hier noch der hinweis auf meinen senf zur ergaenzung
aller schon erfolgten wortmeldungen:
»[Object].equals, [Object].dump, [Object].clone«
so long - peterS. - pseliger@gmx.net
... aehm, *huestel* ... entschuldigung ...
danke fuer den link, Christian - das naechste mal folge ich auch
allen im thtread angegebenen links, um so eine doppelte referenz
zu vermeiden ...
... versprochen - peterS.
Hallo peterS,
»Object.equals, Object.dump, Object.clone«
Das habe ich mir kurz angesehen. Sehr interessant, denn mich interessieren vor allem scheinbar unlösbare Problemee :-). Habe selbst einmal eine object.clone() Methode gebastelt, eine recht einfache, und bin ganz zufrieden damit. Allerdings geschah das nicht im Hinblick auf eine Object.equals() Methode.
Du schreibst dort:
"wer es schafft, fuer JavaScript ein wasserdichtes [Object].clone und ein verlaessliches [Object].equals zu schreiben, muss diese sprache fast schon verinnerlicht haben."
Kannst du nochmal kurz schildern, wo genau das Problem liegt, z.B. für ein "wasserdichtes [Object].clone"? Es gibt zwar viel "Quälcode" (very funny) dort, aber wenig Doku, die erklären würde, wozu die Kapriolen eigentlich gemacht werden.
Danke und Gruß, Don P
gruss Don P,
...
Das habe ich mir kurz angesehen. Sehr interessant, denn mich
interessieren vor allem scheinbar unlösbare Problemee : -)....
Du schreibst dort:
...
Kannst du nochmal kurz schildern, wo genau das Problem liegt,
z.B. für ein "wasserdichtes [Object].clone"? Es gibt zwar viel
"Quälcode" (very funny) dort, aber wenig Doku, die erklären
würde, wozu die Kapriolen eigentlich gemacht werden.
mache ich - vorweg aber erstmal die erklaerungen fuer [equals] und [dump]:
wie schon vielfach erlaeutert, lassen sich JavaScript-objekte genau
dann nicht mehr verlaesslich miteinander vergleichen, sobald fuer
einen vergleich sowohl mit dem gleichheits- [==] als auch mit dem
identitaetsoperator [===] keine objektreferenzen mehr herangezogen
werden koennen.
in diesem fall muss man eine art "signatur" fuer jedes der beiden
objekte fuer den vergleich bemuehen.
diese im folgenden [dump] genannte methode, sollte sich in ihrer
grundfunktionalitaet an die von der mozilla.org implementierten
[toSource]-methode anlehnen, muss aber ueber diese hinaus auch der
tatsache rechnung tragen, dass objekte eben nicht allein durch
initilisierende konstruktor-aufrufe beschrieben werden koennen,
sondern immer auch hash-verhalten an den tag legen.
darueber hinaus erschwert die vermischung von JavaScript-api
(sprachkern) und DOM-api (dokument) das vergleichende geschaeft.
der [dump] beschreibt bzw. identifiziert elemente des DOM deswegen
in dieser reihenfolge anhand ihrer id-, tagName-, name-getter. noch
nicht ins DOM eingefuegte knoten-fragmente muessen ihr [innerHTML]
offenlegen.
fuer [clone] muss aehnlich vorgegangen werden, wenn man nicht der
Crockfordschen/Confordschen*[1] schule folgen will, in der das zu
kopierende object als prototyp eines anonymen und *leeren* konstruktors
referenziert wird - frei nachempfunden, aber prinzipiell so:
var object = (function (obj) {
var cnstr = new Function();
cnstr.prototype = obj;
return (new cnstr());
});
var arr = ["hallo", "welt"];
var obj = object(arr);
alert("arr : " + arr + "\nobj : " + obj + "\n(arr == obj) ? " + (arr == obj));
alert("(arr.toString() === obj.toString()) ? " + (arr.toString() === obj.toString()));
alert("(\"" + arr.join(" ") + "\" === \"" + obj.join(" ") + "\") ? " + (arr.join("! ") === obj.join("! ")));
/*
bitte copy und paste nach [[link:http://jconsole.com/]]
*/
*[1] - Douglas Crockford / Richard Conford
zurueck vom ausflug - weiter mit dem versuch moeglichst ideal zu klonen:
die [clone]-methode wird jedem build-in-typen - [Boolean], [Number], [String],
[Date], [Error], [RegExp], [Function] und schlussendlich allgemein [Object]
(was [Array] mit einschliesst) einzeln *verbacken* - warum?
im (rekursiven) clone-prozess verlaesst man sich zurecht darauf, dass das
weitgehend unbekannte objekt am besten ueber seine instanziierungs-geschichte,
seine prototypen-kette und erst zuletzt ueber seinen konstruktor *bescheid weiss*
und demzufolge die *richtige* clone methode in ebendieser reihenfolge automatisch
abgegriffen werden kann. dabei wird der erfolg einer instanziierung ueberwacht
und zuletzt gegebenenfalls auf den zu erwartenden konstruktor/typ zurueckgegriffen.
im nachfolgenden wiederum rekursiven aufbau der jeweils ersten objektebene
werden ausschliesslich nicht [prototype]-eigenschaften zugelassen. fuer diese
wird nach einer im sinne von JavaScript "nativen"*[2] [clone]-methode gesucht.
scheitert das projekt an dieser stelle kann das ergebnis einer vollstaendigen
iteration nicht mehr als *echter clone" bezeichnet werden, denn die nachfolgenden
fallbacks nehmen dann das was kommt - zuerst wird das nicht native [clone] probiert,
dannach wird nur noch referenziert.
*[2] siehe [this.isNative] aus [http://www.pseliger.de/jsExtendedApi/jsApi.Object.typeDetection.new.dev.js]:
this.isUndefined = function (obj) {
return (typeof obj == "undefined");
};
this.isNull = function (obj) {
return ((typeof obj == "object") && (!obj));
};
this.isValue = function (obj) {
return ((typeof obj == "string") || (typeof obj == "number") || (typeof obj == "boolean"));
};
this.isObject = function (obj) {
return (obj && (typeof obj == "object"));
};
this.isNative = function (obj) {
return (!isUndefined(obj) && !isNull(obj) && (typeof obj.constructor == "function"));
};
this.isAlien = function (obj) {
return (isObject(obj) && (typeof obj.constructor != "function"));
};
so long - peterS. - pseliger@gmx.net