Discussion:
Call by Ref bei Strings
(zu alt für eine Antwort)
Nico Wessels
2007-04-13 23:29:46 UTC
Permalink
Hi NG;

ich würde gerne einer Methode zwei Strings übergeben, die in der Methode
verändert werden, also wird z.B. ein Teil des Strings_2 an String_1
angehängt.

Das wird innerhalb der Methode in etwa so gemacht:

...
string_1 = string_1 + " " + teilvonstring2
...

Leider sind die Werte von String_1 nach dem Return verloren. Ich dachte,
da ich die Referenz bei einem String an die Methode übergebe, kann ich
so etwas machen und das klappt.

Frage daher, wie muss ich für Strings eine Call-By-Reference-Methode
rightig implementieren???
Stefan Ram
2007-04-13 23:38:57 UTC
Permalink
Post by Nico Wessels
Frage daher, wie muss ich für Strings eine Call-By-Reference-Methode
- String-Objekte sind nicht veränderlich
- In Java gibt es nur Call-by-Value

Eine Methode, die zwei Texte verbindet:

java.lang.String cat
( final java.lang.String left, final java.lang.String right )
{ return left + right; }

Siehe auch:

http://www.purl.org/stefan_ram/pub/java_referenzvariablen_als_argument_de
http://www.purl.org/stefan_ram/pub/java_veraenderliche_objekte_de
http://www.purl.org/stefan_ram/pub/java_gebrauch_von_objekten_de
http://www.purl.org/stefan_ram/pub/java_referenzen_de
Achim Peters
2007-04-14 00:17:18 UTC
Permalink
| string_1 = string_1 + " " + teilvonstring2
Post by Stefan Ram
Post by Nico Wessels
Frage daher, wie muss ich für Strings eine Call-By-Reference-Methode
- String-Objekte sind nicht veränderlich
- In Java gibt es nur Call-by-Value
java.lang.String cat
( final java.lang.String left, final java.lang.String right )
{ return left + right; }
Eine andere Methode, die zwei (per binärem Operator) Texte verbindet:

+

etwa in

string_1 = string_1 + " " + teilvonstring2

Die hatte der OP aber schon gefunden.

Bye
Achim
Andreas Eberhöfer
2007-04-14 08:42:07 UTC
Permalink
Post by Stefan Ram
Post by Nico Wessels
Frage daher, wie muss ich für Strings eine Call-By-Reference-Methode
- String-Objekte sind nicht veränderlich
- In Java gibt es nur Call-by-Value
Nur fürs Protokol Java macht nicht Call-by-Value, Java macht
Call-By-Const-Reference, um das zu verdeutlichen was das heißt ein
bisschen C++-Code: (Bitte nicht schlagen mein C++ ist nicht mehr so frisch)

void addToList(List liste, Object o);
Das wäre Call-By-Value würde ich der liste das Object o hinzufügen hätte
das nach Außen keinen Effekt, weil in der Methode auf einer Kopie
gearbeitet wird.

void addToList(List &liste, Object &o);
Das wäre Call-By-Reference hier hätte das Hinzufügen nach Außen auch
eine Wirkung, aber ich kann in der Methode der liste eine anderen Liste
zuweißen.

void addToList(const List &liste, const Object &o);
Das wäre "Call-By-Const-Reference", das hinzufügen sieht man nach Außen,
aber in der Methode kann man die Referenzen der Parameter nicht ändern.
Das ist das was Java macht.

mfg
Andreas
Ralf Ullrich
2007-04-14 10:28:39 UTC
Permalink
Post by Andreas Eberhöfer
Post by Stefan Ram
- String-Objekte sind nicht veränderlich
- In Java gibt es nur Call-by-Value
Nur fürs Protokol Java macht nicht Call-by-Value, Java macht
Call-By-Const-Reference, um das zu verdeutlichen was das heißt ein
bisschen C++-Code: (Bitte nicht schlagen mein C++ ist nicht mehr so frisch)
Nur fürs Protokoll: Die Diskussion ist alt und führt immer wieder zu ewige
Threads. Und nebenbei hast du noch unrecht, denn das ganze ist zwischen
C++ und Java nicht so einfach vergleichbar.

Variablen bezeichnen Speicherplätze. Speicherplätze enthalten Werte. Werte
können zunächst mal primitive Typen sein, wie int, double, boolean etc.
(Wobei C++ hier schon leicht abweicht.)

In Java können Werte darüberhinaus noch Referenzen sein. Allerdings gibt
es, von der Zuweisung mal abgesehen, keinerlei Operationen um die Werte
von Referenzen zu ändern.

In C++ dagegen, können Werte auch Objekte oder Pointer sein. Referenzen
sind dabei Pointer, die lediglich automatisch dereferenziert werden und
sie sind damit etwas ganz anderes, als unter Java unter diesem Wort
verstanden wird.

Was genau ist nun Call-by-Reference?

CbR liegt vor, wenn die aufgerufene Methode einen Pointer auf einen
Speicherstelle erhält, die beim Aufrufer durch eine Variable bezeichnet
wird. Dieser übergebene Pointer wird bei der aufgerufenen Methode i.d.R.
automatisch dereferenziert.

In Java scheitert das schon daran, das es keinerlei Möglichkeit gibt,
einer Methode die Speicherstelle zu übergeben, die von einer Variablen
bezeichnet wird. Es kann immer nur der Wert der Speicherstelle übergeben
werden. Eben Call-by-Value.
Post by Andreas Eberhöfer
void addToList(const List &liste, const Object &o);
Das wäre "Call-By-Const-Reference", [...]. Das ist das was Java macht.
Nö. Das ist das was du in C++ machen kannst, um ein Verhalten zu
produzieren, dass dem von Java ähnelt.

Hast du dich schonmal gefragt warum es in C++ sowohl einen dot- als auch
einen Arrow-Operator gibt und warum Java nur einen der beiden braucht?

Die Antwort darauf ist praktisch diesselbe, wie die Antwort auf die Frage,
warum Java eigentlich allein mit CbV auskommt.

cu
Michael Paap
2007-04-14 12:12:01 UTC
Permalink
Post by Andreas Eberhöfer
Nur fürs Protokol Java macht nicht Call-by-Value, Java macht
Call-By-Const-Reference, um das zu verdeutlichen was das heißt ein
bisschen C++-Code: (Bitte nicht schlagen mein C++ ist nicht mehr so frisch)
Nur fürs Protokoll: Das Thema haben wir hier alle Jahre wieder und
eigentlich enden wir mehr oder weniger immer bei folgender Aussage:

-----------------------------------------------------------------------
In Java werden *alle* Parameter *immer* "by value" übergeben. Ist ein
Parameter eine Objektreferenz, wird *diese Referenz* by value übergeben,
d.h. es wird eine lokale Kopie der Referenz angelegt. Zugriffe über
diese Referenz auf das referenzierte Objekt können Änderungen des
Objektzustandes bewirken, Zuweisungen an die lokale Variable hingegen
bleiben nach außen wirkungslos.
-----------------------------------------------------------------------

Und das war es für mich dann auch bzgl. dieses Themas.

Gruß,
Michael
Andreas Eberhöfer
2007-04-14 15:56:17 UTC
Permalink
Post by Michael Paap
-----------------------------------------------------------------------
In Java werden *alle* Parameter *immer* "by value" übergeben. Ist ein
Parameter eine Objektreferenz, wird *diese Referenz* by value übergeben,
d.h. es wird eine lokale Kopie der Referenz angelegt. Zugriffe über
diese Referenz auf das referenzierte Objekt können Änderungen des
Objektzustandes bewirken, Zuweisungen an die lokale Variable hingegen
bleiben nach außen wirkungslos.
-----------------------------------------------------------------------
Ok wieder mal was dazu gelernt, und ein schönes Beispiel dafür, dass
man nicht alles einfach so schlucken sollte was die Professoren einem
so erzählen ;).

schönes Wochenende
Andreas
Jochen Theodorou
2007-04-14 20:24:07 UTC
Permalink
Post by Andreas Eberhöfer
Post by Michael Paap
-----------------------------------------------------------------------
In Java werden *alle* Parameter *immer* "by value" übergeben. Ist ein
Parameter eine Objektreferenz, wird *diese Referenz* by value übergeben,
d.h. es wird eine lokale Kopie der Referenz angelegt. Zugriffe über
diese Referenz auf das referenzierte Objekt können Änderungen des
Objektzustandes bewirken, Zuweisungen an die lokale Variable hingegen
bleiben nach außen wirkungslos.
-----------------------------------------------------------------------
Ok wieder mal was dazu gelernt, und ein schönes Beispiel dafür, dass
man nicht alles einfach so schlucken sollte was die Professoren einem
so erzählen ;).
in dem Zusammenhang gab es auch schon die Begriffe
"call-by-value-reference" und auch "call-by-object", wobei letztere
irgendwie nicht so auf Java passt, da es auch primitive Datentypen
gibt... das ganze call-by ist eh wenig hilfreich, wenn man nicht genau
weiss was es macht und da ist so eine längere Erklärung sehr viel
hilfreicher als missverständlich eingesetzte Terminologie.

Gruss theo
--
Jochen "blackdrag" Theodorou
Groovy Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/
Nico Wessels
2007-04-16 20:38:28 UTC
Permalink
Post by Michael Paap
-----------------------------------------------------------------------
In Java werden *alle* Parameter *immer* "by value" übergeben. Ist ein
Parameter eine Objektreferenz, wird *diese Referenz* by value übergeben,
d.h. es wird eine lokale Kopie der Referenz angelegt. Zugriffe über
diese Referenz auf das referenzierte Objekt können Änderungen des
Objektzustandes bewirken, Zuweisungen an die lokale Variable hingegen
bleiben nach außen wirkungslos.
-----------------------------------------------------------------------
Genau aus diesem Grund, hatte ich das bei Strings auch erwartet, aber da
war wohl was falsch im Gedächtnis
Bernd Eckenfels
2007-04-14 14:00:56 UTC
Permalink
Post by Andreas Eberhöfer
Nur fürs Protokol Java macht nicht Call-by-Value, Java macht
Call-By-Const-Reference, um das zu verdeutlichen was das heißt ein
bisschen C++-Code: (Bitte nicht schlagen mein C++ ist nicht mehr so frisch)
Java macht Call-by-Value. Wobei Values nur primitive Typen oder Referenzen
sind. Du kannst das nicht mit C++ Begriffen beschreiben, weil Java kein C++
ist.

Gruss
Bernd
Nico Wessels
2007-04-16 20:35:19 UTC
Permalink
Post by Stefan Ram
java.lang.String cat
( final java.lang.String left, final java.lang.String right )
{ return left + right; }
http://www.purl.org/stefan_ram/pub/java_referenzvariablen_als_argument_de
http://www.purl.org/stefan_ram/pub/java_veraenderliche_objekte_de
http://www.purl.org/stefan_ram/pub/java_gebrauch_von_objekten_de
http://www.purl.org/stefan_ram/pub/java_referenzen_de
Ich finde die Seiten hier eigentlich sehr praktisch und sie wären auch
sehr informativ, aber die Formatierung/Einrückung. Hast Du nicht Lust
die nach alter Eclipse-Manier zu formatieren (also im Prinzip alles in
eine extra Zeile etc., Klassen-Namen ohne Packages am Anfang in ner
Methode -> so bekommt man schneller den Blick aufs Wesentlich)?

Ich habe schon ein paar sehr gute Beiträge von Dir gefunden, aber das
fand ich immer ein bisschen schade. Das bitte nicht als schlechte
Kritik, sondern eher als konstruktive Kritik verstehen!!!
Stefan Ram
2007-04-16 22:22:12 UTC
Permalink
Post by Nico Wessels
Ich finde die Seiten hier eigentlich sehr praktisch und sie
wären auch sehr informativ, aber die Formatierung/Einrückung.
Es freut mich, daß Dir die Seiten - abgesehen von der
Formatierung - gefallen!

Grundsätzlich hätte ich nichts dagegen, sie, wie von Eclipse
formatiert, zu formatieren, solange ich dies intern nicht
selber verwenden muß. Dazu müßte ich eine automatische
Umformatierung für Webseiten und Usenet-Artikel aufbauen - was
grundsätzlich geplant ist, aber leider noch dauern wird.

Bis dahin ist es für die Eclipse-Formatierung-Anhänger
einfacher, einen von mir veröffentlichten Quellcode von
Eclipse umformatieren zu lassen (ein Tastendruck), als es für
mich ist, einen Eclipse-formatierten Quellcode in meine
Konvention rückzuübersetzen (Handarbeit).
Stefan Ram
2007-04-14 00:12:55 UTC
Permalink
Post by Nico Wessels
Frage daher, wie muss ich für Strings eine Call-By-Reference-Methode
cum grano salis:

public class Main
{
public static void add
( final String accumulator, final java.lang.String addend )
{ accumulator.value += addend; }

public static void main( final java.lang.String[] args )
{ String s = new String( "alpha" );
add( s, "beta" );
java.lang.System.out.println( s ); }}

class String
{ public java.lang.String value;
public String( final java.lang.String value ){ this.value = value; }
public java.lang.String toString(){ return this.value; }}
Stefan Ram
2007-04-14 03:12:44 UTC
Permalink
Die einfachste und naheliegendste Lösung fällt
mir natürlich wieder einmal als letzte ein.

public class Main
{
public static void main( final java.lang.String[] args )
throws java.lang.NoSuchFieldException, java.lang.IllegalAccessException
{ final java.lang.String s = "epsilon"; add( s, "zeta" );
java.lang.System.out.println( s ); }

public static void add
( final java.lang.String augend, final java.lang.String addend )
throws java.lang.NoSuchFieldException, java.lang.IllegalAccessException
{ final java.lang.String sum = augend + addend;
set( augend, "value", get( sum, "value", new char[ 0 ]));
set( augend, "count", get( sum, "count", 0 ));
set( augend, "offset", get( sum, "offset", 0 ));
set( augend, "hash", get( sum, "hash", 0 )); }

public static< O, T >void set
( final O object, final java.lang.String name, final T value )
throws java.lang.NoSuchFieldException, java.lang.IllegalAccessException
{ final java.lang.reflect.Field field =
java.lang.String.class.getDeclaredField( name );
{ final boolean old = field.isAccessible();
field.setAccessible( true );
field.set( object, value );
field.setAccessible( old ); }}

@SuppressWarnings( "unchecked" )
public static< O, T >T get
( final O object, final java.lang.String name, final T value_ )
throws java.lang.NoSuchFieldException, java.lang.IllegalAccessException
{ final java.lang.reflect.Field field =
java.lang.String.class.getDeclaredField( name );
T value = null;
{ final boolean old = field.isAccessible();
field.setAccessible( true );
value = (( Class< T >)( value_.getClass() )).cast( field.get( object ));
field.setAccessible( old ); }
return value; }}
Stefan Ram
2007-04-14 04:15:50 UTC
Permalink
Post by Stefan Ram
public static< O, T >void set
public static< O, T >T get
Etwas kürzer ist hier eine einzelne Übertragungsmethode:

public class Main
{
public static void main( final java.lang.String[] args )
throws java.lang.NoSuchFieldException, java.lang.IllegalAccessException
{ final java.lang.String s = "eta"; add( s, "theta" );
java.lang.System.out.println( s ); }

public static void add
( final java.lang.String augend, final java.lang.String addend )
throws java.lang.NoSuchFieldException, java.lang.IllegalAccessException
{ transfer( augend, augend + addend ); }

public static< O >void transfer
( final O target, final O source )
throws java.lang.NoSuchFieldException, java.lang.IllegalAccessException
{ for( final java.lang.reflect.Field field:
java.lang.String.class.getDeclaredFields())
{ if( !java.lang.reflect.Modifier.isStatic( field.getModifiers() ))
{ final boolean old = field.isAccessible();
field.setAccessible( true );
field.set( target, field.get( source ));
field.setAccessible( old ); }}}}
Achim Peters
2007-04-14 00:16:26 UTC
Permalink
Post by Nico Wessels
ich würde gerne einer Methode zwei Strings übergeben, die in der Methode
verändert werden, also wird z.B. ein Teil des Strings_2 an String_1
angehängt.
...
string_1 = string_1 + " " + teilvonstring2
...
Leider sind die Werte von String_1 nach dem Return verloren. Ich dachte,
da ich die Referenz bei einem String an die Methode übergebe, kann ich
so etwas machen und das klappt.
Da Java Methoden-Parameter immer by-value übergibt, könntest Du
lediglich das verändern, auf was die übergebene Referenz zeigt. Zum
einen veränderst Du aber oben nicht das, auf was die Referenz zeigt,
sondern die Referenz selbst, lässt sie also auf etwas neues zeigen, was
sich - wg. "by-value" - nie nach außen auswirkt, zum anderen sind gerade
Strings, also das, auf was die Referenz bei Dir zeigt, aber per se nicht
veränderbar, so dass es keine Methode à la
void String#add_string(String) gibt.

Du kannst also den Parameter string_1 innerhalb der Methode nicht
verändern (weil es ausgerechnet und ausnahmsweise der Typ String nicht
zulässt), sondern müsstest ausnahmsweise, wie von Stefan IMO etwas
verklausuliert dargestellt, das Ergebnis über den Return-Wert der
Funktion zurückliefern und das den Aufrufer dann string_1 zuweisen lassen.

HTH

Bye
Achim
Post by Nico Wessels
Frage daher, wie muss ich für Strings eine Call-By-Reference-Methode
rightig implementieren???
Nico Wessels
2007-04-14 11:50:22 UTC
Permalink
Post by Achim Peters
Du kannst also den Parameter string_1 innerhalb der Methode nicht
verändern (weil es ausgerechnet und ausnahmsweise der Typ String nicht
zulässt), sondern müsstest ausnahmsweise, wie von Stefan IMO etwas
verklausuliert dargestellt, das Ergebnis über den Return-Wert der
Funktion zurückliefern und das den Aufrufer dann string_1 zuweisen lassen.
Okay, aber leider kann ich das nicht über den Return-Wert machen, das
ganze läuft als Algorithmus in etwa so ab:

Nehme zwei Strings und schaue, ob der Anfang des zweiten Strings (z.B. 2
Tokens) nicht zum zweiten String gehören darf, sondern eigentlich zum
Ende des ersten Strings gehören müsste. Ist das der Fall, dann lösche
diese Tokens aus dem zweiten String und hänge sie ans Ende des ersten
Strings an.

Da ich dies gerne in eine Methode kapseln würde, ergibt sich eben das
Problem, dass man nicht zwei Werte zurückgeben darf, sondern am liebsten
die übergebenen Werte per Referenz übergeben will und diese dann verändert.
Ralf Ullrich
2007-04-14 12:49:10 UTC
Permalink
Post by Nico Wessels
Nehme zwei Strings und schaue, ob der Anfang des zweiten Strings (z.B. 2
Tokens) nicht zum zweiten String gehören darf, sondern eigentlich zum Ende
des ersten Strings gehören müsste. Ist das der Fall, dann lösche diese
Tokens aus dem zweiten String und hänge sie ans Ende des ersten Strings an.
Da ich dies gerne in eine Methode kapseln würde, ergibt sich eben das
Problem, dass man nicht zwei Werte zurückgeben darf, sondern am liebsten
die übergebenen Werte per Referenz übergeben will und diese dann verändert.
Und schon haben wir deinen Fehler: Falsche Datenstruktur! Du hast eben
nicht Strings (=fixe Zeichenketten), sondern veränderliche Token-Listen.

void algorithm(List<Token> first, List<Token> second) {
while ((second.size()>0) && !second.get(0).isAllowedAtBegin()) {
first.addLast(second.removeFirst());
}
}


class Token {
boolean isAllowedAtBegin() {...}
...
}

cu
Nico Wessels
2007-04-16 20:41:49 UTC
Permalink
Post by Ralf Ullrich
Und schon haben wir deinen Fehler: Falsche Datenstruktur! Du hast eben
nicht Strings (=fixe Zeichenketten), sondern veränderliche Token-Listen.
void algorithm(List<Token> first, List<Token> second) {
while ((second.size()>0) && !second.get(0).isAllowedAtBegin()) {
first.addLast(second.removeFirst());
}
}
Genau das war mein Fehler. Ich hab ich Grunde schon fixe Strings, die
Tokenisierung, muss ich leider relativ mühsam (im Sinne von vermutlich
zeitaufwändig) über String.split(" ") machen.
Bernd Eckenfels
2007-04-16 18:44:47 UTC
Permalink
Post by Nico Wessels
Genau das war mein Fehler. Ich hab ich Grunde schon fixe Strings, die
Tokenisierung, muss ich leider relativ mühsam (im Sinne von vermutlich
zeitaufwändig) über String.split(" ") machen.
Das einmal zu machen ist aber nicht so schlimm wie staendig neue zu bauen
und zu parsen.

Gruss
Bernd
Stefan Ram
2007-04-14 13:09:26 UTC
Permalink
das >Problem, dass man nicht zwei Werte zurückgeben darf
In einer Sprache mit Tupelergebnissen, könnte man eine
Operation »sumdiff« implementieren, welche die Summe und
die Differenz zweier Werte als Ergebniss liefern könnte.

operation "sumdiff"
in x, y;
out s, d;
{ s = x + y; d = x - y; }

Das folgenden Programm zeigt eine Annäherung in Java.
Dabei wird die Operation »sumdiff« generisch formuliert,
so daß sie für jeder Klasse mit einer Addition und
Subtraktion eingesetzt werden kann.

public class Main
{ public static void main( final java.lang.String[] args )
{ final Sumdiff<Int> result = new Sumdiff<Int>
( new Int( 4 ), new Int( 2 )); result.calculate();
java.lang.System.out.println( result.getSum() );
java.lang.System.out.println( result.getDifference() ); }}

class Sumdiff
<T extends AddOperation<T> & SubtractOperation<T> >
{ public Sumdiff( final T x, final T y )
{ this.x = x; this.y = y; }
public void calculate()
{ this.sum = x.add( y ); this.difference = x.subtract( y ); }
public T getSum(){ return sum; }
public T getDifference(){ return difference; }
final T x; final T y; T sum; T difference; }

interface AddOperation<T>{ T add( T t ); }
interface SubtractOperation<T>{ T subtract( T t ); }

class Int implements AddOperation<Int>, SubtractOperation<Int>
{ final int value;
public Int( final int value )
{ this.value = value; }
public Int add( final Int other )
{ return new Int( this.value + other.value ); }
public Int subtract( final Int other )
{ return new Int( this.value - other.value ); }
public java.lang.String toString()
{ return java.lang.String.valueOf( this.value ); }}

6
2

Eine andere Möglichkeit ist es, eine Klasse mit zwei
/öffentlichen/ Komponenten einzuführen, aus denen dann
ein weiteres Objekt erzeugt wird, daß die destruktiven
Operationene implementiert. Da »result« dabei eine
Variable des Klienten ist, werden möglicherweise als
»aufwendig« empfundene Methodenaufrufe, wie »getX()« zum
Erhalt der beiden Komponten vermieden. Gleichzeitig kann
»Server« voll gekapselt bleiben (»result« hat dort
das Attribut »private«).

class Result { public int x; public int y; }

class Server
{
private Result result;

public Server( final Result result )
{ this.result = result; }

void calculate( final int x, final int y )
{ this.result.x = x + y; this.result.y = x - y; }}

class Main
{
public static void main( final java.lang.String[] args )
{ Result result = new Result();
Server server = new Server( result );
server.calculate( 1, 2 );
java.lang.System.out.println( result.x );
java.lang.System.out.println( result.y );
server.calculate( 3, 4 );
java.lang.System.out.println( result.x );
java.lang.System.out.println( result.y ); }}

Eine Variante zeigt, wie eine einzelne Methode »server«
»zwei Ergebnisse liefern« kann:

class Result { public int x; public int y; }

class Example
{
void server( final Result result, final int x, final int y )
{ result.x = x + y; result.y = x - y; }}

private Result result = new Result(); /* might be placed in block below */

void client()
{ server.run( result, 1, 2 );
java.lang.System.out.println( result.x );
java.lang.System.out.println( result.y );
server.run( result, 3, 4 );
java.lang.System.out.println( result.x );
java.lang.System.out.println( result.y ); }}

Das nächste Java-Programm zeigt, wie in Example#main einem »Aktor«
»Server« eine Fortsetzung »this« übergeben wird, an die er
dann sein Ergebnis sendet. Da die Fortsetzung zum aufrufenden
Objekt gehört, ist damit eine »Rückkehr« in dieses Objekt,
aber nicht die aufrufende Methode/Stelle verbunden. Nur ist
dies ohne spezielle Implementation von Endrekursion nur das
halbe Vergnügen. Man kann in Java nicht alle Möglichkeiten
dieses Ansatzes konsequent ausschöpfen.

Dadurch kann jedenfalls die Methode »getPair« ebenfalls »zwei
Zahlen als Ergebnis« (hier zwei Zufallswerte) liefern, die
dann durch den Aufruf der »continuation« wieder an den
Klienten der Klasse »Example« »zurückgegeben« werden.

interface Client { void continuation( int x, int y ); }

class Server
{ static java.util.Random rand = new java.util.Random();
static void getPair( final Client client )
{ client.continuation( rand.nextInt( 11 ), rand.nextInt( 21 )); }}

class Example implements Client
{ public void continuation( final int x, final int y )
{ java.lang.System.out.println( x + ", " + y ); }
public void main()
{ Server.getPair( this ); }}

public class Main
{ public static void main( final java.lang.String[] args )
{ new Example().main(); }}
Stefan Matthias Aust
2007-04-14 14:09:56 UTC
Permalink
Post by Stefan Ram
In einer Sprache mit Tupelergebnissen, könnte man eine
Operation »sumdiff« implementieren, welche die Summe und
die Differenz zweier Werte als Ergebniss liefern könnte.
operation "sumdiff"
in x, y;
out s, d;
{ s = x + y; d = x - y; }
Das folgenden Programm zeigt eine Annäherung in Java.
Wie meistens ist auch hier Scala das bessere Java, denn diese
JVM-Sprache kennt Tuple als eingebaute Datentypen:

// Funktion definieren, man beachte, dass return, { oder } unnötig sind
def sumdiff(x: int, y: int) = (x + y, x - y)

// Funktion aufrufen und Ergebnisse parallel an zwei Variablen binden
val (a, b) = sumdiff(3, 4)

Um Stefans generifiziertes Beispiel nachzuahmen, müsste man in Scala

def sumdiff[t <: ...](x: t, y: t): t = ...
trait Add[t] { def +(t: t): t }
trait Sub[t] { def -(t: t): t }

// Definiert Klasse inkl. Konstruktor und Factory "X"
case class X(i: int) extends Add[int] with Sub[int] {
def +(x: int) = i + x
def -(x: int) = i - x
}

val (a, b) = sumdiff(X(1), X(2))

schreiben, wobei ich jetzt zu faul bin, die Syntax für die
Typeinschränkung nachzugucken. Aber dieser Weg ist hier nicht nötig.

Möchte man etwa Exemplare der Klasse

// Definiert Klasse inklusive Konstruktor und Getter "value"
class MyInt(val value: int)

mit sumdiff addieren, und das, obwohl MyInt nicht von int erbt und auch
kein + und - zur Verfügung stellt, kann man Konverter-Fuktionen
definieren, die MyInt auf int zurückrechnen:

implicit def toInt(MyInt i) = i.value

(Ich meine, der Konverter kann auch in der Klasse selbt definiert
werden, also

class MyInt(val value: int) { implicit def toInt = value }

und eigentlich müsste dann konsequenterweise auch

class MyInt(implicit val value: int)

gehen, aber ich will das jetzt nicht nachgucken)

Jetzt akzeptiert der Compiler jedenfalls:

val (a, b) = sumdiff(new MyInt(1), new MyInt(2))

Ich könnte auch dafür sorgen, dass a und b wieder Exemplare von MyInt
sind, doch dann müsste ich das Tuplepattern mit Typconstraints versehen
und das sieht häßlich aus.
--
Stefan Matthias Aust
Stefan Ram
2007-04-14 20:09:57 UTC
Permalink
Nur ist dies ohne spezielle Implementation von
Endrekursion nur das halbe Vergnügen.
Ein interessante Übersicht zur Implementation
unbeschränkter Endrekursion in C gibt

http://home.pipeline.com/~hbaker1/CheneyMTA.html

Eine Technik verwendet longjmp (das wir in Java
nicht haben), um den C-Keller aufzulösen und
eine andere verwendet einen alloca-Aufruf mit
einem negativen Argument! »alloca« ist allerdings
kein Teil von ISO-C.

In Java kann man allenfalls die normale
»Trampolin«-Technik verwenden, die dort knapp
aber gut erklärt wird.
Stefan Ram
2007-04-14 20:14:46 UTC
Permalink
Post by Stefan Ram
In Java kann man allenfalls die normale
»Trampolin«-Technik verwenden, die dort knapp
aber gut erklärt wird.
Korrektur: Das wird in Java auch schwierig.
Stefan Matthias Aust
2007-04-15 20:44:33 UTC
Permalink
Post by Stefan Ram
In Java kann man allenfalls die normale
»Trampolin«-Technik verwenden, die dort knapp
aber gut erklärt wird.
Das Actor-Rahmenwerk von Scala bräuchte eigentlich eine Optimierung von
endrekursiven Funktionsaufrufen. Da Java das bekanntlich nicht macht,
räumen die einfach - IIRC - von Zeit zu Zeit den Stack mit einer
Exception ab. Das scheint ein guter Kompromiß aus Ausführungs-
geschwindigkeit und Einfachheit zu sein.
--
Stefan Matthias Aust
Stefan Ram
2007-04-15 21:00:36 UTC
Permalink
Post by Stefan Matthias Aust
Das Actor-Rahmenwerk von Scala bräuchte eigentlich eine
Optimierung von endrekursiven Funktionsaufrufen. Da Java das
bekanntlich nicht macht, räumen die einfach - IIRC - von Zeit
zu Zeit den Stack mit einer Exception ab. Das scheint ein guter
Kompromiß aus Ausführungsgeschwindigkeit und Einfachheit zu sein.
Darauf wäre/bin ich wieder einmal nicht gekommen!

»throw« ist ja so gesehen eine Entsprechung von »longjmp«.
Stefan Ram
2007-04-17 01:25:16 UTC
Permalink
Post by Stefan Ram
Post by Stefan Matthias Aust
zu Zeit den Stack mit einer Exception ab. Das scheint ein guter
Kompromiß aus Ausführungsgeschwindigkeit und Einfachheit zu sein.
Darauf wäre/bin ich wieder einmal nicht gekommen!
Noch etwas zu Stack-Manipulationen auf einer JVM:

»A Technique for Implementing First-Class Continuations«

http://eval.apply.googlepages.com/stackhack4.html

(Habe ich selber aber (noch?) nicht richtig gelesen.)
Jochen Theodorou
2007-04-17 09:11:04 UTC
Permalink
Post by Stefan Ram
Post by Stefan Ram
Post by Stefan Matthias Aust
zu Zeit den Stack mit einer Exception ab. Das scheint ein guter
Kompromiß aus Ausführungsgeschwindigkeit und Einfachheit zu sein.
Darauf wäre/bin ich wieder einmal nicht gekommen!
»A Technique for Implementing First-Class Continuations«
http://eval.apply.googlepages.com/stackhack4.html
(Habe ich selber aber (noch?) nicht richtig gelesen.)
die machen was javaflow zum beispiel macht, den stack kopieren, die
methoden umschreiben sodass sie den gespeicherten Stack als zusätzliches
Objekt akzeptieren usw... Das herausspringen wird dann mit einer
Exception simuliert.... Nichts neues.

Gruss theo
--
Jochen "blackdrag" Theodorou
Groovy Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/
Michael Klemm
2007-04-18 17:29:41 UTC
Permalink
Post by Stefan Ram
Post by Stefan Matthias Aust
Das Actor-Rahmenwerk von Scala bräuchte eigentlich eine
Optimierung von endrekursiven Funktionsaufrufen. Da Java das
bekanntlich nicht macht, räumen die einfach - IIRC - von Zeit
zu Zeit den Stack mit einer Exception ab. Das scheint ein guter
Kompromiß aus Ausführungsgeschwindigkeit und Einfachheit zu sein.
Darauf wäre/bin ich wieder einmal nicht gekommen!
»throw« ist ja so gesehen eine Entsprechung von »longjmp«.
Ja und Nein. :-)

Java (und auch C++) können nicht mit longjmp funktionieren, sondern
brauchen ein etwas mächtigeres Konzept (= Ausnahmen). Der Grund hierfür
sind in Java u.a. Locks, finally-Blöcke und bei C++ u.a. automatische
Objektvariablen.

Ich nur einfache Sprachen wie C können soetwas einfaches wie longjmp
(zumindest in der Implementierung der libc) sinnvoll unterstützen.

Viele Grüße
-michael
--
Post by Stefan Ram
Nenne mir ein Wort und ich erkläre Dir, daß es griechischen Ursprungs
ist
Na dann: Semmelknödel und Wolpertinger
(Anastasios Tsitlakidis und Michael Rauscher in d.c.l.j)
Kai Schwebke
2007-04-14 03:32:26 UTC
Permalink
Post by Nico Wessels
...
string_1 = string_1 + " " + teilvonstring2
...
Leider sind die Werte von String_1 nach dem Return verloren. Ich dachte,
Du änderst die ByValue-übergebene Referenz, nicht das
referenzierte Object. Dies geht bei Strings auch nicht,
da diese nicht änderbar sind.

Möglich ist z.B.:

public class StringTest {

public static void append1(StringBuffer s) {
s.append(" World!");
}

public static void append2(String[] s) {
s[0] += " World!";
}

public static void main(String args[]) {
StringBuffer s1 = new StringBuffer();
s1.append("Hello");
append1(s1);
System.out.println(s1.toString());

String[] s2 = new String[1];
s2[0] = "Hello";
append2(s2);
System.out.println(s2[0]);
}

}

Wenn der übergeben String noch oft in der Methode verändert wird, sollte
auf jeden Fall mit einem StringBuffer gearbeitet werden, da damit
weniger temporäre String-Objekte erzeugt werden.


Kai
Heiner Kücker
2007-04-14 06:22:20 UTC
Permalink
Kai Schwebke schrieb
Post by Kai Schwebke
public class StringTest {
Fehlt hier nicht die Member-Variable ?

StringBuffer s = new StringBuffer();
Post by Kai Schwebke
public static void append1(StringBuffer s) {
s.append(" World!");
}
public static void append2(String[] s) {
s[0] += " World!";
}
Vielleicht auch so

package util;

/**
* Hilfs-Klasse für Input-Output-Parameter oder
* mehrere Rückgabewerte beim Methoden-Aufruf.
*
* Dadurch kann ein String verändert aus
* einer Methode herausgegeben werden.
* Die Verwendung dieser Klasse ist
* besser lesbar als die Benutzung
* von Arrays als Objekt-Behälter.
*
* @author Heiner Kücker
*/
public class MutableString
implements Cloneable, Comparable
{
/**
* der gewrappte immutable String.
*/
public String str;

/**
* Constructor.
*/
public MutableString()
{
super();
}

/**
* Constructor.
*
* @param pStr
*/
public MutableString(
final String pStr)
{
this.str = pStr;
}

/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public int hashCode()
{
final int PRIME = 31;
int result = 1;
result = PRIME * result + ((this.str == null) ? 0 :
this.str.hashCode());
return result;
}

/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final MutableString other = (MutableString) obj;
if (this.str == null)
{
if (other.str != null)
return false;
}
else if (!this.str.equals(other.str))
return false;
return true;
}

/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString()
{
return String.valueOf( this.str );
}

/* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(
final Object pO)
{
return CompareUtil.compareTo( this , (Comparable) pO);
}

}
--
Heiner Kücker
www.heinerkuecker.de
Ralf Ullrich
2007-04-14 10:41:49 UTC
Permalink
Post by Nico Wessels
Hi NG;
ich würde gerne einer Methode zwei Strings übergeben, die in der Methode
verändert werden,
Java Strings sind unveränderlich. Daher können wir schon hier abbrechen
und sagen: "Was du gerne machen würdest geht einfach nicht. Werd' damit
fertig."
Post by Nico Wessels
Post by Nico Wessels
Frage daher, wie muss ich für Strings eine Call-By-Reference-Methode
rightig implementieren???
unterliegt noch immer dem Fehler, das Strings veränderbar seien (mal ganz
abgesehen von dem Problem, das Java kein CbR kennt). Die einzige Antwort
darauf kann wieder nur sein: "Das geht nicht!". D.h. die ganze Diskussion
um CbR/CbV können wir uns sparen. Und möglichst auch die Diskussion, wie
man es am besten simuliert.

Stattdessen sollten wir uns fragen: "Warum hättest du das denn gerne
gemacht?"

Sprich wir sollten uns deinem _Problem_ zuwenden, und es für Java lösen,
und nicht, wie du es jetzt machst, dein Problem für eine andere Sprache
mit deren Mitteln, nämlich CbR-Methoden und veränderliche Strings, lösen,
und dann auf Java portieren. (Die anderen Threads zeigen bislang nur
hilflose Versuche deine Lösung auf Java zu portieren, weil sie alle dein
eigentliches Problem nicht kennen.)

Also was ist dein Problem, das du gerne gelöst hättest?

cu
Sven Köhler
2007-04-14 16:15:56 UTC
Permalink
Post by Nico Wessels
Frage daher, wie muss ich für Strings eine Call-By-Reference-Methode
rightig implementieren???
Wurde schon häufig gesagt, aber ich sag's nochmal:
In Java werden Referenzen (Pointer) auf Objekte per Call-By-Value übergeben.


Übergib einfach eine Referenz auf einen StringBuffer (oder ab Java 1.5
StringBuilder). Das ist mehr oder weniger ein String, der auch
modifiziert werden darf.
Wanja Gayk
2007-04-15 12:41:36 UTC
Permalink
Post by Nico Wessels
Hi NG;
ich würde gerne einer Methode zwei Strings übergeben, die in der Methode
verändert werden, also wird z.B. ein Teil des Strings_2 an String_1
angehängt.
...
string_1 = string_1 + " " + teilvonstring2
...
Leider sind die Werte von String_1 nach dem Return verloren. Ich dachte,
da ich die Referenz bei einem String an die Methode übergebe, kann ich
so etwas machen und das klappt.
Frage daher, wie muss ich für Strings eine Call-By-Reference-Methode
rightig implementieren???
Nimm eien StringBuilder:

StringBuilder add(final StringBuilder sb, final String teilString){
sb.append(teilString);
return sb;
}

Das Ergebnis befindet sich immer StringBuilder und kann per #toString()
immer zum String umgeformt werden (es bietet sich aber an den
StringBuilder erstmal weiter zu verwenden).

Gruß,
-Wanja-
--
Ada Byron, die Lochkarten für Babbages "Difference Engine" vorbereitete,
gilt als die erste Programmiererin der Weltgeschichte. Sie hat auch das
Verhaltensmuster für alle nachfolgenden Programmierer vorgegeben: Sie
trank und schluckte Drogen.
Nico Wessels
2007-04-16 20:43:27 UTC
Permalink
Post by Wanja Gayk
StringBuilder add(final StringBuilder sb, final String teilString){
sb.append(teilString);
return sb;
}
Das Ergebnis befindet sich immer StringBuilder und kann per #toString()
immer zum String umgeformt werden (es bietet sich aber an den
StringBuilder erstmal weiter zu verwenden).
So hab ichs jetzt gemacht
Paul Ebermann
2007-04-15 17:27:13 UTC
Permalink
Post by Nico Wessels
Frage daher, wie muss ich für Strings eine Call-By-Reference-Methode
rightig implementieren???
Hmm, ich dachte eigentlich, du seiest schon lange genug
in dieser Newsgroup aktiv, um mitbekommen zu haben, dass
es in Java kein Call-By-Reference gibt.


In Java gibt es _nur_ Call-By-Value. Punkt.


Du kannst ein Call-By-Reference simulieren, indem du ein
Objekt übergibst, welches die zu manipulierende Variable
enthält, anstatt die Variable selbst (d.h. ihren Wert) zu
übergeben, und dann deren Variablen zu manipulieren.
(In deinem Fall bietet sich da ein String[] an.)


Eine häufig bessere Lösung ist es, ein Objekt zu verwenden,
welches direkt veränderbar ist (z.B. durch Aufruf passender
Methoden), in der Methode sind dann keine Zuweisungen zum
Parameter (bzw. zu Komponenten des als Parameter übergebenen
Objektes) zu finden, sondern nur Methodenaufrufe dieses
Objektes. Bei dir würden sich StringBuilder anbieten, wie
etwa Ralf schon vorgeschlagen hat.


Paul
--
Nun ludigxas: : ()
Nico Wessels
2007-04-16 20:45:24 UTC
Permalink
Post by Paul Ebermann
Hmm, ich dachte eigentlich, du seiest schon lange genug
in dieser Newsgroup aktiv, um mitbekommen zu haben, dass
es in Java kein Call-By-Reference gibt.
Hhhm ja, man macht manchmal auch noch im Alter Fehler ;-)
Post by Paul Ebermann
In Java gibt es _nur_ Call-By-Value. Punkt.
Der Punkt war eben, dass ich einen Datentyp-String als ein
veränderliches Objekt angesehen hatte und dachte, wenn ich ein Objekt
übergebe, kann ich dieses auch innerhalb der Methoder verändern.
Bernd Eckenfels
2007-04-16 18:49:02 UTC
Permalink
Post by Nico Wessels
Der Punkt war eben, dass ich einen Datentyp-String als ein
veränderliches Objekt angesehen hatte und dachte, wenn ich ein Objekt
übergebe, kann ich dieses auch innerhalb der Methoder verändern.
Der Denkfehler faellt aber bei String (oder Integer) spätestens dann auf,
wenn es keine change oder set methode gibt..

Gruss
Bernd
Paul Ebermann
2007-04-18 17:04:24 UTC
Permalink
Post by Nico Wessels
Post by Paul Ebermann
In Java gibt es _nur_ Call-By-Value. Punkt.
Der Punkt war eben, dass ich einen Datentyp-String als ein
veränderliches Objekt angesehen hatte und dachte, wenn ich ein Objekt
übergebe, kann ich dieses auch innerhalb der Methoder verändern.
Ja, wenn du eine passende Methode gefunden hättest, den Inhalt
zu verändern, würde das auch gehen. Zuweisen zu der lokalen
Variable ist keine solche Methode.

Hier etwas, was nur zum Ausprobieren ist, keinesfalls zum
Verwenden in irgendeiner produktiven Umgebung:


---
package de.dclj.paul.newsgroup;
import java.lang.reflect.Field;

class Probier
{
public void changeString(String hallo, String neuerInhalt)
throws IllegalAccessException, NoSuchFieldException
{
Field valueField = String.class.getDeclaredField("value");
Field offsetField = String.class.getDeclaredField("offset");
Field countField = String.class.getDeclaredField("count");
valueField.setAccessible(true);
offsetField.setAccessible(true);
countField.setAccessible(true);
char[] data = neuerInhalt.toCharArray();
valueField.set(hallo, data);
offsetField.setInt(hallo, 0);
countField.setInt(hallo, data.length);
}

public static void main(String[] egal)
throws IllegalAccessException, NoSuchFieldException
{
String beispiel = "Beispiel";
System.out.println("Vorher: " + beispiel);
new Probier().changeString(beispiel, "Neuer Text");
System.out.println("Nachher: " + beispiel);
System.out.print("Und jetzt eine Überraschung: ");
System.out.println("Beispiel");
System.out.println("So funktioniert es nicht: " +
"Beispiel");
}
}
---

In einer kritischen Umgebung verhindert so etwas hoffentlich
der Security-Manager.


Paul
--
Nun ludigxas: : ()
Michael Klemm
2007-04-18 17:35:10 UTC
Permalink
Post by Paul Ebermann
Du kannst ein Call-By-Reference simulieren, indem du ein
Objekt übergibst, welches die zu manipulierende Variable
enthält, anstatt die Variable selbst (d.h. ihren Wert) zu
übergeben, und dann deren Variablen zu manipulieren.
(In deinem Fall bietet sich da ein String[] an.)
Die Betonung muss hierbei aber auf "simulieren" liegen, weil damit
bestenfalls Call-by-Copy/Return erreicht wird. In Java ist kein echtes
Call-by-Reference für primitive Datentypen möglich, weil der direkte
Zugang zur entsprechenden Speicherstelle verwehrt wird.

Viele Grüße
-michael
--
Post by Paul Ebermann
Nenne mir ein Wort und ich erkläre Dir, daß es griechischen Ursprungs
ist
Na dann: Semmelknödel und Wolpertinger
(Anastasios Tsitlakidis und Michael Rauscher in d.c.l.j)
Lesen Sie weiter auf narkive:
Loading...