Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Information rekursiv suchen

4 views
Skip to first unread message

Sascha Schäfer

unread,
Jun 14, 2006, 8:45:02 PM6/14/06
to
Hallo,

Meine Ausgangssituation ist folgendermaßen. Ich möchte mir Informationen
sammeln, die in einem objektorientierten Datenmodell gespeichert sind,
welches in Java programmoert ist. Dies tue ich rekursiv. Ich nehme die
Attributwerte der Objekte, speichere diese bei Bedarf und untersuche dann
die Attributwerte der Attributwerte. Das solange, bis ich auf atomare
Attributewerte treffe, die selbst keine Attribute mehr haben.

Gibt es für das rekursive Vorgehen Alternativen? Mein Problem ist nämlich,
dass eine java.lang.StackOverflowError-Exception geworfen wird. Ich denke,
dass die Ursache darin liegt, dass ich zu tief in der Suchebene bin, quasi
zu viele rekursive Aufrufe meiner Suchmethode getätigt habe. Kann das sein?
Mögliche Zirkelverweise im Objektgraphen habe ich berücksichtigt und
unterbunden? Auch Objekte, die untersucht wurden, werden nicht nochmals
untersucht.

Mit freundlichen Grüßen

Sascha


Message has been deleted

Ralf Ullrich

unread,
Jun 14, 2006, 9:21:46 PM6/14/06
to
Sascha Schäfer wrote:

>Gibt es für das rekursive Vorgehen Alternativen? Mein Problem ist nämlich,
>dass eine java.lang.StackOverflowError-Exception geworfen wird. Ich denke,
>dass die Ursache darin liegt, dass ich zu tief in der Suchebene bin, quasi
>zu viele rekursive Aufrufe meiner Suchmethode getätigt habe. Kann das sein?
>Mögliche Zirkelverweise im Objektgraphen habe ich berücksichtigt und
>unterbunden? Auch Objekte, die untersucht wurden, werden nicht nochmals
>untersucht.

Also erstmal, wenn du StackOverflow bekommst, dan stehen die Chancen gut,
dass du doch bei der Erkennung der "Zirkelverweise" etwas falsch gemacht
hast. Aber egal, wenn du deinen Code wie folgt aufbaust, kann kaum was
schiefgehen:


// hier beginnst du deine Suche:
Object root = ...;

// hierin sind alle bereits bekannten Objekts:
HashSet known = new HashSet();

// hier speicherst du welche Objekte du noch besuchen musst:
LinkedList todo = new LinkedList();

// wir müssen noch die root durchsuchen, und sie ist uns bekannt:
todo.addLast(root);
known.add(root);

// solange wir was zu tun haben, bleiben wir in einer schleife:
while (!todo.isEmpty()) {

// Wir entfernen das erste Element der Todo-Liste,
// es ist unser aktuelles Objekt:
Object current = todo.removeFirst();

// Wir erzeugen eine Liste direkter Nachfolger des
// aktuellen Objekts (in deinem Fall also alle noch
// zu besuchenden Attribute, die Methode könnte außerdem
// die dich interessierenden Objekte wegspeichern)
List children = getChildren(current);

// Wir gehen nun in einer inneren Schleife über alle Kinder
for(Object child : children) {

// alle noch nicht bekannten Kinder, kommen in die todo-Liste:
if (known.add(child)) {
todo.addLast(child);
}
}
}

// der set wird nun nicht mehr gebraucht, verbraucht aber
// viel Speicher, daher löschen wir ihn:
known.clear();

Das wars. Wie du siehst, keine Rekursion mehr, dafür aber etwas an
Heap-Verbrauch, doch davon ist ja meist reichlich vorhanden.

HTH

BTW: Der Algorithmus kann so oft gebraucht werden, dass du ihn auch leicht
in eine abstrakte Klasse packen kannst, bei der root im Konstruktor
übergeben wird, und getChildren zunächst abstrakt ist und erst von der
abgeleiteten Klasse implementiert wird. Dann könntest du in deinem
Programm dort, wo du ihn brauchst einfach schreiben:

final Set resultSet = ...
(new Traversal(root) {
protected List getChildren(Object current) {
if (interesting) {
resultSet.add(current)
}
...
List retval = new List();
...
retval.add(aChild);
...
return retval;
}
}).run();


cu

Sascha Schäfer

unread,
Jun 15, 2006, 5:44:00 AM6/15/06
to

"Ralf Ullrich" <ne...@jnana.de> schrieb im Newsbeitrag
news:xn0enhsp5...@news.online.de...


> Also erstmal, wenn du StackOverflow bekommst, dan stehen die Chancen gut,
> dass du doch bei der Erkennung der "Zirkelverweise" etwas falsch gemacht
> hast. Aber egal, wenn du deinen Code wie folgt aufbaust, kann kaum was
> schiefgehen:
>
>
> // hier beginnst du deine Suche:
> Object root = ...;

Soweit so gut.

>
> // hierin sind alle bereits bekannten Objekts:
> HashSet known = new HashSet();
>
> // hier speicherst du welche Objekte du noch besuchen musst:
> LinkedList todo = new LinkedList();

Zwei Collection-Objekte habe ich ebenfalls. Sie haben den gleichen Zweck wie
deine. Ich habe sie selbstverständlich als globale Variablen deklariert.
Jedesmal, wenn ich ein neues Objekt untersuche, quasi die Methode neu
gerufen wurde, dann wird am Anfang der Methode dieses Objekt der todo-Liste
hinzugefügt. Zuvor überprüfe ich natürlich, ob das Objekt etweder in der
know-Set oder in der todo-Liste schon vorhanden ist. Wenn ja, dann wird die
Methode sofort abgebrochen.
Also dürften niemals "Zirkelverweise" auftreten, oder?

>
> // wir müssen noch die root durchsuchen, und sie ist uns bekannt:
> todo.addLast(root);
> known.add(root);
>
> // solange wir was zu tun haben, bleiben wir in einer schleife:
> while (!todo.isEmpty()) {
>
> // Wir entfernen das erste Element der Todo-Liste,
> // es ist unser aktuelles Objekt:
> Object current = todo.removeFirst();
>
> // Wir erzeugen eine Liste direkter Nachfolger des
> // aktuellen Objekts (in deinem Fall also alle noch
> // zu besuchenden Attribute, die Methode könnte außerdem
> // die dich interessierenden Objekte wegspeichern)
> List children = getChildren(current);
>
> // Wir gehen nun in einer inneren Schleife über alle Kinder
> for(Object child : children) {
>
> // alle noch nicht bekannten Kinder, kommen in die todo-Liste:
> if (known.add(child)) {
> todo.addLast(child);
> }
> }
> }

Aber wie untersuche ich die Kinder der Kinder? Wenn ich in der inneren
Schleife ein Kind untersuche, dann müsste ich getChildren(child) aufrufen,
um dessen Kinder zu bekommen. Und dann wieder und wieder. Wie geht das ohnen
Rekursion?

mfg
Sascha


Sascha Broich

unread,
Jun 15, 2006, 7:23:36 AM6/15/06
to
On Thu, 15 Jun 2006 11:44:00 +0200, Sascha Schäfer wrote:

> Aber wie untersuche ich die Kinder der Kinder? Wenn ich in der inneren
> Schleife ein Kind untersuche, dann müsste ich getChildren(child) aufrufen,
> um dessen Kinder zu bekommen. Und dann wieder und wieder. Wie geht das ohnen
> Rekursion?

Schauen wir uns nochmal folgenden Ablauf an:

> "Ralf Ullrich" <ne...@jnana.de> schrieb im Newsbeitrag

>> // solange wir was zu tun haben, bleiben wir in einer schleife:
>> while (!todo.isEmpty()) {

Die Todo-Liste hält die noch nicht besuchten Elemente.
Hier startet auch die Schleife, die nicht(!) rekursiv ist, weil sie pro
Durchlauf - immer in der selben Stack-Höhe(!) - genau ein Element und nur
dessen Kinder betrachtet.

>> // Wir entfernen das erste Element der Todo-Liste,
>> // es ist unser aktuelles Objekt:
>> Object current = todo.removeFirst();

Nun gibts ein Element weniger in der Todo-Liste.

>> // Wir erzeugen eine Liste direkter Nachfolger des
>> // aktuellen Objekts (in deinem Fall also alle noch
>> // zu besuchenden Attribute, die Methode könnte außerdem
>> // die dich interessierenden Objekte wegspeichern)
>> List children = getChildren(current);

Auch die Methode getChildren arbeitet mit genau einem Element und nur mit
dessen Kindern. Dabei passiert ebenfalls keine Rekursion.

>> // Wir gehen nun in einer inneren Schleife über alle Kinder
>> for(Object child : children) {

Hier werden wiederum keine rekursiven Aufrufe gemacht, sonder nur eine
Liste abgearbeitet.

>> // alle noch nicht bekannten Kinder, kommen in die todo-Liste:
>> if (known.add(child)) {

Wenn das Kind noch nicht bekannt ist, wird es erfolgreich der Known-Liste
hinzugefügt.

>> todo.addLast(child);

Und daraufhin wird das Kind der Todo-Liste hinzugefügt. Also nur noch nicht
besuchte Kinder werden in die Todo-Liste aufgenommen.

>> }
>> }

In der Todo-Liste befinden sich am Ende der Schleife nur Elemente, die noch
nicht besucht wurden.

>> }

Jetzt gibt es keine noch nicht besuchten Elemente mehr.


Das Verfahren nennt sich Breitensuche (BSF):
<http://de.wikipedia.org/wiki/Breitensuche>

Sascha Broich
--
Zynismus ist das Schwert,
Sarkasmus das Rapier und
Ironie das Florett
im Wortgefecht. (S. Word)

Sascha Schäfer

unread,
Jun 15, 2006, 10:20:44 AM6/15/06
to
Alles klar. Vielen Dank für deine super Hilfe.


mfg
Sascha


Sascha Schäfer

unread,
Jun 15, 2006, 11:07:37 AM6/15/06
to
Eine kurze Frage habe ich doch noch. Wie verhält es sich mit
Objektsammlungen und Maps. Gebe ich die Elemente ebenfalls in der
getChildren-Methode zurück???


mfg
Sascha


Sascha Schäfer

unread,
Jun 15, 2006, 2:21:38 PM6/15/06
to
Hallo nochmal,

Ich habe den Algorithmus aus deinem Lösungsvorschlag programmiert. Leider
bekomme ich immer noch einen StackOverflowError. Ich bin jetzt echt am
verzweifeln. Im Folgenden sind die beiden programmierten Methoden
aufgelistet:

private void traverse(Object root, Map staticValues)
{
if (root == null)
return;

if (Util.isPrimitive(root))
return;
if (root instanceof String)
return;

List children = getChildrenAndCollectValues(root, staticValues);
for (Iterator iter = children.iterator(); iter.hasNext();)
{
Object child = iter.next();
if (child != null &&
!m_todo.contains(child) &&
!m_known.contains(child) &&
!Util.isPrimitive(child) &&
!(child instanceof String))
m_todo.add(child);
}
m_known.add(root);

Object current;
while(!m_todo.isEmpty())
{
current = m_todo.poll();
children = getChildrenAndCollectValues(current, staticValues);
for (Iterator iter = children.iterator(); iter.hasNext();)
{
Object child = iter.next();
if (child != null &&
!m_todo.contains(child) &&
!m_known.contains(child) &&
!Util.isPrimitive(child) &&
!(child instanceof String))
m_todo.add(child);
}
m_known.add(current);
}
}

private List getChildrenAndCollectValues(Object obj, Map staticValues)
{
List children = new LinkedList();

if (obj instanceof Collection)
{
for (Iterator iter = ((Collection) obj).iterator(); iter.hasNext();)
{
Object element = iter.next();
children.add(element);
}
}
else if (obj instanceof Map)
{
Map map = (Map) obj;
for (Iterator iter = map.keySet().iterator(); iter.hasNext();)
{
Object key = iter.next();
children.add(key);
Object value = map.get(key);
children.add(value);
}
}
else if (obj.getClass().isArray())
{
for (int i = 0; i < Array.getLength(obj); i++)
{
Object element = Array.get(obj, i);
children.add(element);
}
}

if (obj.getClass().getClassLoader() != null && !obj.getClass().isArray())
{
Field[] fields = Reflection.getFieldsExceptSDK(obj.getClass());
for (int i = 0; i < fields.length; i++)
{
Field f = fields[i];

try
{
Object value = Reflection.getValue(obj, f);
if (!Modifier.isFinal(f.getModifiers()) &&
Modifier.isStatic(f.getModifiers()))
{
staticValues.put(f, value);
//System.out.println("stored static value: " + key);
}
System.out.println("Field: " + f.getName());
children.add(value);
}
catch (IllegalAccessException e)
{
System.err.println("Can't store static value of the attribute " +
f.getName() + " in the class " + obj.getClass());
}
}
}
return children;
}

Die gesamte Vorgang soll zum Speichern von statischen Attributwerten dienen.


mfg
Sascha


Message has been deleted

Ralf Ullrich

unread,
Jun 15, 2006, 4:04:26 PM6/15/06
to
Sascha Schäfer wrote:

>Hallo nochmal,
>
>Ich habe den Algorithmus aus deinem Lösungsvorschlag programmiert. Leider
>bekomme ich immer noch einen StackOverflowError. Ich bin jetzt echt am
>verzweifeln. Im Folgenden sind die beiden programmierten Methoden
>aufgelistet:
>

[...Saschas Code...]


Wie konntest du nur meinen Code so verhunzen. Im Ernst, was du da
geschrieben hast, ist nicht mehr derselbe Algorithmus. Um dir das besser
erklären zu können muss ich aber erst mal den Code etwas umstellen, damit
er leichter lesbar wird:

Fangen wir mal damit an:

if (root == null)
return;
if (Util.isPrimitive(root))
return;
if (root instanceof String)
return;

Wie wir noch sehen werden brauchen wir diese drei Tests öfter, also machen
wir daraus eine Methode:

private boolean invalid(Object o) {
return (o == null) || Util.isPrimitive(o) || (o instanceof String);
}

Statt drei "if () return haben wir dann nur noch eins:

if (invalid(root)) {
return;
}


Zum nächsten Abschnitt:

List children = getChildrenAndCollectValues(root, staticValues);
for (Iterator iter = children.iterator(); iter.hasNext();)
{
Object child = iter.next();
if (child != null &&
!m_todo.contains(child) &&
!m_known.contains(child) &&
!Util.isPrimitive(child) &&
!(child instanceof String))
m_todo.add(child);
}
m_known.add(root);

Das würde ich (zunächst) anders schreiben:

List children = getChildrenAndCollectValues(root, staticValues);
for (Iterator iter = children.iterator(); iter.hasNext();) {
Object child = iter.next();

if (child == null)
continue;
if (Util.isPrimitive(child))
continue;
if (child instanceof String)
continue;
if (!m_todo.contains(child) && !m_known.contains(child))
m_todo.add(child);
}
m_known.add(root);

Wie wir sehen sind da wieder die drei Tests, also machen wir daraus:

List children = getChildrenAndCollectValues(root, staticValues);
for (Iterator iter = children.iterator(); iter.hasNext();) {
Object child = iter.next();

if (invalid(child)) {
continue;
}
if (!m_todo.contains(child) && !m_known.contains(child))
m_todo.add(child);
}
m_known.add(root);

Machen wir weiter, du hast da stehen:

Object current;
while (!m_todo.isEmpty()) {
current = m_todo.poll();

Warum vergrößerst du den Scope von current? Die Variable wird außerhalb
der Schleife nicht gebraucht. Falls du dachtest, dass sei eine
Optimierung, dann irrst du. Desweiteren, wenn du hier plötzlich poll() zum
Auslesen verwendest, dann solltest du konsequenterwiese auch offer()
verwenden um Objekte hinzuzufügen, denn add() gehört nicht zum
Queue-Interface.

Es kommt dann nochmal fast derselbe Code wie schon oben:

children = getChildrenAndCollectValues(current, staticValues);
for (Iterator iter = children.iterator(); iter.hasNext();) {
Object child = iter.next();
if (child != null && !m_todo.contains(child)
&& !m_known.contains(child) && !Util.isPrimitive(child)
&& !(child instanceof String))
m_todo.add(child);
}
m_known.add(current);

den ich wieder gleichermaßen vereinfacht habe:

children = getChildrenAndCollectValues(current, staticValues);
for (Iterator iter = children.iterator(); iter.hasNext();) {
Object child = iter.next();

if (invalid(child)) {
continue;
}
if (!m_todo.contains(child) && !m_known.contains(child))
m_todo.add(child);
}
m_known.add(current);


Alles in allem sieht dein Code hier jetzt also so aus:

private boolean invalid(Object o) {
return (o == null) || Util.isPrimitive(o) || (o instanceof String);
}

private void traverse(Object root, Map staticValues) {

if (invalid(root)) {
return;
}

List children = getChildrenAndCollectValues(root, staticValues);
for (Iterator iter = children.iterator(); iter.hasNext();) {
Object child = iter.next();

if (invalid(child)) {
continue;
}
if (!m_todo.contains(child) && !m_known.contains(child))
m_todo.add(child);
}
m_known.add(root);

Object current;
while (!m_todo.isEmpty()) {


current = m_todo.poll();
children = getChildrenAndCollectValues(current, staticValues);
for (Iterator iter = children.iterator(); iter.hasNext();) {
Object child = iter.next();

if (invalid(child)) {
continue;
}
if (!m_todo.contains(child) && !m_known.contains(child))
m_todo.add(child);
}
m_known.add(current);
}
}

Wo bei meinem Vorschlag also steht:

todo.addLast(root);
known.add(root);
...
if (known.add(child)) {
todo.addLast(child);
}

steht bei dir effektiv:

m_todo.addAll(validChildren);
m_known.add(root);
...
m_todo.addAll(validChildren);
m_known.add(current);


Und das ist eben nicht mehr dasselbe. Keine Ahnung ob der Algorithmus mit
diesen Änderungen von dir überhaupt noch funkioniert, aber da du einen
StackOverflow bekommst, würde ich mal sagen, dass er nicht mehr
funktioniert. Der wichtigste Unterschied ist wohl:

während bei mir alle Objekte, die in todo stehen auch bereits in known
stehen, werden bei dir die Objekte erst dann nach known übernommen, wenn
sie aus todo wieder herausgeholt wurden. Daher musst du beim einfügen nach
todo auch todo.contains und known.contains aufrufen, während bei mir der
test mit known alleine ausreichend ist.


Hier nun nochmal mein Vorschlag, nachdem ich ihn zunächst korrigiert habe:

private boolean invalid(Object o) {
return (o == null) || Util.isPrimitive(o) || (o instanceof String);
}

private void traverseCorrect(Object root, Map staticValues) {
if (invalid(root)) {
return;
}

m_todo.add(root);
m_known.add(root);

while (!m_todo.isEmpty()) {
Object current = m_todo.poll();
List children = getChildrenAndCollectValues(current, staticValues);


for (Iterator iter = children.iterator(); iter.hasNext();) {
Object child = iter.next();

if (invalid(child)) {
continue;
}
if (m_known.add(child)) {
m_todo.add(child);
}
}
}
}

Und da mich ankotzt wie der Code von getChildrenAndCollectValues
ausschaut, mache ich dir sogar noch eine komplette geschönte Variante:

private void traverse(Object root, Map result) {
if (!valid(root)) {
return;
}

m_todo.add(root);
m_known.add(root);

while (!m_todo.isEmpty()) {
Object current = m_todo.poll();

List children = visit(current, result);


for (Iterator iter = children.iterator(); iter.hasNext();) {
Object child = iter.next();

if (m_known.add(child)) {
m_todo.add(child);
}
}
}
}

private boolean valid(Object o) {
return (o != null) && !Util.isPrimitive(o) && !(o instanceof String);
}

private List visit(Object obj, Map staticValues) {


List children = new LinkedList();

if (obj instanceof Collection) {
collectValidElements((Collection) obj, children);


} else if (obj instanceof Map) {

collectValidElements(((Map) obj).keySet(), children);
collectValidElements(((Map) obj).values(), children);
} else if (obj.getClass().isArray()) {
collectValidElements(Arrays.asList(obj), children);
}

if (obj.getClass().getClassLoader() != null
&& !obj.getClass().isArray()) {

collectFieldMembers(obj, staticValues, children);
}
return children;
}

private void collectValidElements(Collection c, List children) {
for (Iterator iter = c.iterator(); iter.hasNext();) {
Object element = iter.next();
if (!(!valid(element))) {
children.add(element);
}
}
}

private void collectFieldMembers(Object obj, Map staticValues, List children) {


Field[] fields = Reflection.getFieldsExceptSDK(obj.getClass());
for (int i = 0; i < fields.length; i++) {
Field f = fields[i];

try {
Object value = Reflection.getValue(obj, f);
if (!Modifier.isFinal(f.getModifiers())
&& Modifier.isStatic(f.getModifiers())) {
staticValues.put(f, value);
// System.out.println("stored static value: " +

// key);


}
System.out.println("Field: " + f.getName());
children.add(value);
} catch (IllegalAccessException e) {
System.err.println("Can't store static value of the attribute "
+ f.getName() + " in the class " + obj.getClass());
}
}
}


BTW: Darf ich mal fragen ob du auch schon mal ein Buch übers Programmieren
gelesen hast? (Ich mein nicht übers Lernen einer Programmiersprache, ich
meine übers Programmieren!) Wenn nicht, wird's Zeit. Mein Favorit "Rodney
Zaks - Pascal and UCSD Pascal" ist leider total out of date, aber
vielleicht kennen ja vielleicht die lüngeren in der Runde hier eine
modernere Alternative.

Desweiteren solltest du dir unbedingt den Klassiker "Sedgewick -
Algorithmen" zulegen.


Und zum Schluß noch: Gewöhn dir bitte solche Variablenen wie "m_todo" und
"m_known" im Java Umfeld ganz schnell wieder ab. (Wenn du natürlich mit
völlig verdrehten Corporate Code Conventions zu tun hast, kannst du wenig
dagegen tun.)

cu

Sascha Schäfer

unread,
Jun 15, 2006, 6:39:04 PM6/15/06
to

"Stefan Ram" <r...@zedat.fu-berlin.de> schrieb im Newsbeitrag
news:keller-200...@ram.dialup.fu-berlin.de...

> "Sascha Schäfer" <ders...@t-online.de> writes:
> >Leider bekomme ich immer noch einen StackOverflowError.
>
> Da steht auf dem Bildschirm ja nicht nur »StackOverflowError«,
> sondern auch eine Darstellung des Kellerspeichers (stack trace).
> Sie zeigt dir an, wo rekuriert wurde.
>

Also die Ausgabe des StackTrace half mir gar nicht weiter. Du meinst doch
sicherlich folgende Ausgabe:

Exception in thread "main" java.lang.StackOverflowError

at java.util.HashMap$KeyIterator.<init>(Unknown Source)

at java.util.HashMap$KeyIterator.<init>(Unknown Source)

at java.util.HashMap.newKeyIterator(Unknown Source)

at java.util.HashMap$KeySet.iterator(Unknown Source)

at java.util.HashSet.iterator(Unknown Source)

at java.util.AbstractSet.hashCode(Unknown Source)

at java.util.HashMap$Entry.hashCode(Unknown Source)

at java.util.AbstractMap.hashCode(Unknown Source)

at java.util.AbstractList.hashCode(Unknown Source)

at java.util.AbstractList.hashCode(Unknown Source)

at java.util.AbstractList.hashCode(Unknown Source)

at java.util.AbstractList.hashCode(Unknown Source)

usw.

Also ich als Laie kann mir keine helfenden Informationen heraussuchen.

mfg

Sascha


Message has been deleted

Ralf Ullrich

unread,
Jun 15, 2006, 7:00:17 PM6/15/06
to
Sascha Schäfer wrote:


OK, ich ahne was: Du hast doch m_todo als Field eines bestimmten Objekts
angelegt, richtig?

Frage: Bist du sicher, dass der Graph ausgehend von root nicht auf dieses
Objekt zurückverweist?

Wenn das nämlich der Fall ist, landet die Liste irgendwann in sich selbst,
kurz gesagt passiert also so etwas:

java.util.ArrayList list = new java.util.ArrayList();
list.add(list);
list.hashCode();

Und das führt mit Sicherheit zum StackOverflowError.

cu

Message has been deleted

Sascha Schäfer

unread,
Jun 16, 2006, 4:48:35 AM6/16/06
to
> BTW: Darf ich mal fragen ob du auch schon mal ein Buch übers Programmieren
> gelesen hast? (Ich mein nicht übers Lernen einer Programmiersprache, ich
> meine übers Programmieren!) Wenn nicht, wird's Zeit. Mein Favorit "Rodney
> Zaks - Pascal and UCSD Pascal" ist leider total out of date, aber
> vielleicht kennen ja vielleicht die lüngeren in der Runde hier eine
> modernere Alternative.

So im ganzen, ehrlich gesagt, nein. Bin halt kein Informatikstudent.

>
> Desweiteren solltest du dir unbedingt den Klassiker "Sedgewick -
> Algorithmen" zulegen.

Mach ich. Hoffentlich gibts das günstig gebraucht bei Amazon oder eBay.

>
>
> Und zum Schluß noch: Gewöhn dir bitte solche Variablenen wie "m_todo" und
> "m_known" im Java Umfeld ganz schnell wieder ab. (Wenn du natürlich mit
> völlig verdrehten Corporate Code Conventions zu tun hast, kannst du wenig
> dagegen tun.)

Das ist die Idee meiner Professur gewesen. Aber warum eigentlich?! Ich finde
das gar nicht so unpraktisch. Da erkennt man sofort, ob die Variable lokal
oder global ist. Oder gibts dafür eine bessere Lösung?


Übrigens, mit deiner Annahme in dem anderen Beitrag, dass ich irgendwie auf
meine m__todo-Liste komme, hattest du recht. Ich hatte mir als Kontrolle die
Namen der Felder ausgegeben, die gerade untersucht werden. Aber bevor ich
deinen Algorithmus implementiert hatte, kam die Ausgabe gar nicht soweit.
Erst danach wurde mir der entsprechende Feldname angezeigt und da habe ich
den Fehler erkannt. Jetzt geht alles!

Also vielen Dank für deine erstklassige Hilfe. Ich bin jetzt um einiges
schlauer. Neue Bücher werde ich mir auch zulegen.


mfg
Sascha


Sascha Schäfer

unread,
Jun 16, 2006, 4:49:23 AM6/16/06
to
Danke für deine Hilfe. Ich hab jetzt die Lösung.


mfg
Sascha


Message has been deleted

Ralf Ullrich

unread,
Jun 16, 2006, 5:37:34 AM6/16/06
to
Sascha Schäfer wrote:

>>BTW: Darf ich mal fragen ob du auch schon mal ein Buch übers Programmieren
>>gelesen hast? (Ich mein nicht übers Lernen einer Programmiersprache, ich
>>meine übers Programmieren!) Wenn nicht, wird's Zeit. Mein Favorit "Rodney
>>Zaks - Pascal and UCSD Pascal" ist leider total out of date, aber
>>vielleicht kennen ja vielleicht die lüngeren in der Runde hier eine
>>modernere Alternative.
>
>So im ganzen, ehrlich gesagt, nein. Bin halt kein Informatikstudent.

Ich auch nicht. Bins auch nie gewesen. (Leider.) Aber: Programmieren kann
man auch lernen ohne Informatik zu studieren. Und ich bin mir bis heute
nicht sicher, ob es beim Erlernen des "Programmierhandwerks", nun eher
hilfreich oder schädlich ist, ein Informatik Diplom anzustreben, denn ich
kenne mindestens ebensoviele gute Programmierer, die schlechte
Informatiker sind, wie ich gute Informatiker kenne, die schlechte
Programmierer sind. Nur bei einem bin ich mir sicher: Es gibt weitaus mehr
schlechte Informatiker, die auch schlechte Programmierer sind, als es gute
Informatiker gibt, die auch gute Programmierer sind. Aber das sagt ja
leider nicht viel aus. Sorry, wenn ich etwas abschweife. ;-)


>
>>
>>Desweiteren solltest du dir unbedingt den Klassiker "Sedgewick -
>>Algorithmen" zulegen.
>
>Mach ich. Hoffentlich gibts das günstig gebraucht bei Amazon oder eBay.

Da bin ich mir fast sicher.


>
>>
>>
>>Und zum Schluß noch: Gewöhn dir bitte solche Variablenen wie "m_todo" und
>>"m_known" im Java Umfeld ganz schnell wieder ab. (Wenn du natürlich mit
>>völlig verdrehten Corporate Code Conventions zu tun hast, kannst du wenig
>>dagegen tun.)
>
>Das ist die Idee meiner Professur gewesen. Aber warum eigentlich?! Ich
>finde
>das gar nicht so unpraktisch. Da erkennt man sofort, ob die Variable lokal
>oder global ist. Oder gibts dafür eine bessere Lösung?

Also bei mir sind Field Member blau, Parameter ocker und lokale Variablen
orange.

Aber ich habe mich vielleicht undeutlich ausgedrückt: Ich habe nichts
gegen eine sinnvolle Variante der ungarischen Notation (siehe:
http://de.wikipedia.org/wiki/Ungarische_Notation ), Die "Systems
Hungarian" Notation gehört nun ausgerechnet nicht dazu.

Was mich eigentlich stört ist der Javv-Code-Conventions untypische
Unterstrich im Namen.

Wenn schon, dann sollte m_todo in Java als mTodo geschrieben werden, da
man in Java grundsätzlich CamelCase dem Unterstrich vorzieht.

>Übrigens, mit deiner Annahme in dem anderen Beitrag, dass ich irgendwie auf
>meine m__todo-Liste komme, hattest du recht. Ich hatte mir als Kontrolle
>die
>Namen der Felder ausgegeben, die gerade untersucht werden. Aber bevor ich
>deinen Algorithmus implementiert hatte, kam die Ausgabe gar nicht soweit.
>Erst danach wurde mir der entsprechende Feldname angezeigt und da habe ich
>den Fehler erkannt. Jetzt geht alles!
>
>Also vielen Dank für deine erstklassige Hilfe. Ich bin jetzt um einiges
>schlauer. Neue Bücher werde ich mir auch zulegen.

Dann bin ich froh, dass ich helfen konnte. Was ich von der Sinnhaftigkeit
deines Codes halte, kannst du ja in unserem anderen Thread über Reflection
nachlesen. ;-) Aber trotzdem, nur aus Interesse: Was fängst du denn mit
der Liste der static final Member an?

cu

Ralf Ullrich

unread,
Jun 16, 2006, 6:09:25 AM6/16/06
to
Stefan Ram wrote:

>"Ralf Ullrich" <ne...@jnana.de> writes:
>>Mein Favorit "Rodney Zaks - Pascal and UCSD Pascal" ist leider
>>total out of date, aber vielleicht kennen ja vielleicht die
>>lüngeren in der Runde hier eine modernere Alternative.
>

> Bücher passen zu einem bestimmten Erkenntnisstand
> des Lesers. Vielleicht wärst Du enttäuscht, wenn Du
> es heute noch einmal lesen wolltest.
>
> Ich lese gerade eine Besprechung dieses Buches in
>
>http://www.moorecad.com/standardpascal/pug_newsletter_21.pdf
>
> auf Seite 4 (PDF-Seite 10).

Da bin ich mir sogar sicher, dass ich heute enttäuscht wäre. Aber Zaks
beschreibt in diesem Buch eben anhand von Pascal, was sind Anweisungen,
was Ausdrücke. Was ist eine Sequenz von Anweisungen, wie können sie mit
Kontrollanweisungen strukturiert werden. Was sind variablen, wie
funktionieren Zeiger, wie baut man komplexe Datenstrukturen, wie liest man
Syntaxdiagramme, usw.

D.h. Zaks beschränkt sich eben nicht, wie viele (alle?) heutige
"Programmiersprachen-Lernbücher", darauf die Syntax und evtl. API (resp.
Built-In Funktionen/Anweisungen) einer Sprache zu beschreiben, sondern
benutzt die Sprache quasi nur als Vehikel um grundlegende Konzepte der
Programmierung zu transportieren. Und das ist eigentlich der Part, für den
ich kein modernes Pendant in Buchform kenne. (Ich habe allerdings auch nie
ernsthaft danach gesucht. Wozu auch? Ich kenne das ja nun schon.)
Jedenfalls kenne ich kein modernes Pendant, für das prozedurale Paradigma,
das ja trotz Objektorientierung für Java, C++, C#, etc. immer noch
Grundlage ist.

Zu der Review, auf die du verwiesen hast: Sie trifft völlig zu. Aber sie
ist eben aus der Sicht eines Informatikers geschrieben, sprich mit dem
Blick von oben und einer Menge Theorie im Hinterkopf. Geschrieben wurde
das Buch aber ausdrücklich für Jedermann. Daher ist es nur logisch, dass
ausgerechnet die Teile fehlen (wie in der Review bemängelt), die den
Buch-Inhalt in den größeren Kontext theoretischen Hintergrundwissens
setzen. Sie wären wenig hilfreich bis abschreckend für Jedermann.

Aber was solls. Ich denke ich brauche hier nicht ein 25 Jahre altes Buch
verteidigen, dass heute praktisch keine Relevanz mehr hat.

Viel interessanter, wäre doch zu wissen, ob es ein modernes Buch gibt,
dass das beschriebene leistet, das man dann evtl. Zukunft Anfängern ans
Herz legen kann.

Aber vielleicht ist die einzige Lösung fürs "richtig" Programmieren
lernen, doch die, einmal im Leben ein größeres Programm in Assembler zu
schreiben. Jedenfalls stelle ich fest, dass die meisten schlechten
Programmierer, die ich kenne, niemals in Assembler programmiert haben.

cu

Gerd K.

unread,
Jun 16, 2006, 7:03:41 AM6/16/06
to
Hallo!

> Nur bei einem bin ich mir sicher: Es gibt weitaus mehr
> schlechte Informatiker, die auch schlechte Programmierer sind, als es gute
> Informatiker gibt, die auch gute Programmierer sind. Aber das sagt ja
> leider nicht viel aus. Sorry, wenn ich etwas abschweife. ;-)

*g*
Auch bezgl. Informatik vs. Programmieren gilt folgender Satz, den
ich als Student gehasst, Jahre später geliebt (weil am eigenen
Leibe erfahren) habe ;-))

Der Weg von der Theorie zur Praxis ist eine Frage des Wollens,
der Weg von der Praxis zur Theorie eine Frage des Könnens!

An anderer Stelle hatte ich das schon geschrieben:
Programmiersprachen sind Modeerscheinungen, Grundkonzepte hin-
gegen sind zeitlos. Jemand, der grundlagenorientiert ausgebildet
ist, ist einem reinem Praktiker mindestens mittelfristig immer
überlegen. (Zeigt sich spästestens dann, wenn die Programmiersprache
gewechselt wird *g* ) Er muss nur Wollen ;-)
Oder etwas anders ausgedrückt: Wenn man dem Akademiker gezeigt hat,
das man den Besen mit der breiten Seite nach unten hält, wird
er effektiver putzen als eine nichtakademischer Besenverwender. ;-))

Man kann Grundkonzepte nicht lernen, indem man ein Buch über eine
Programmierpsrache durcharbeitet, denn hier findet der Lernende
zu viel technischen Schnickschnack, der von den wesentlichen
Grundkonzepten ablenkt oder diesen sogar widerspricht (Stichwort:
unbedingte Sprünge ;-) )

Welcher oo-Entwickler macht sich denn heute noch die Mühe, die
Grundlagen der strukturierten Programmierung zu erlernen? "Wieso
denn? Brauch ich nicht mehr..." ist dann immer zu hören. Dass
man aber auf Methodenebene quasi strukturiert programmiert, wird
verkannt. Auch da sollte man z.B. die Grundregel einhalten, dass
statische Programmstruktur und dynamischer Programmfluss
übereinstimmen sollten! Auch da sollte man wissen, wann man welchen
Schleifentyp verwendet und dass das Verlassen einer for-Schleife
mit exit o.ä. mit sehr hoher Wahrscheinlichkeit auf die
Wahl eines falschen Typs hindeutet ;-)

Die ideale Ausbildung eines Programmieres sieht m.E. so aus:
1. Schritt: Sauberes Erarbeiten der zeitlosen Grundkonzepte
mit Hilfe einer Pseudo-Sprache oder einer Sprache, die nicht viel
mehr bietet...
2. Schritt: So schnell wie möglich praktische Erfahrungen mit diesen
Konzepten sammeln, d.h. dem Lernenden eine "Entwicklungsumgebung"
geben, und erklären, wie die verwendete Sprache diese Konzepte
umsetzt. (Sonst nichts von der Sprache erklären...)
3. Schritt: Viiieeel später ;-) Erklären, was die Sprache
noch bietet und warum es unter seltenen Umständen sinnvoll sein kann,
von den Grundkonzepten abzuweichen.

Falls der Lernende noch keine Kenntnisse in oo hat, bietet sich m.E.
die OOA an, um ihn in die Welt der Objektorientiertheit einzuführen.
(0. Schritt)
D.h. er lernt die Modellierung der Realwelt! Hier findet
man alle Konzepte, die man auch in OOP braucht. Echter Mehrwert
eines solchen Vorgehens: Der Lernenden lernt über den Tellerrand
eines Programmieres hinauszuschauen.

Grüße,
Gerd

Message has been deleted

Ralf Ullrich

unread,
Jun 16, 2006, 1:02:04 PM6/16/06
to
Stefan Ram wrote:

>"Ralf Ullrich" <ne...@jnana.de> writes:
>>D.h. Zaks beschränkt sich eben nicht, wie viele (alle?) heutige
>>"Programmiersprachen-Lernbücher", darauf die Syntax und evtl.
>>API (resp. Built-In Funktionen/Anweisungen) einer Sprache zu
>>beschreiben, sondern benutzt die Sprache quasi nur als Vehikel
>>um grundlegende Konzepte der Programmierung zu transportieren.
>

> Es gibt eine interessante Anmerkung zum Wort »Konzept« in
>
>http://www.aifb.uni-karlsruhe.de/WBS/dob/docs/informatik.pdf
>
> auf Seite 17 (unten) und besonders Seite 18. Kurz gesagt
> ist das richtige Wort wahrscheinlich »Begriff«.
>
> Ich glaube, daß es weniger »grundlegende Konzepte der
> Programmierung« gibt, als man gemeinhin annimmt, denn schon
> Wörter wie »Variable« oder »Objekt« bedeuten in Java und C++
> ganz Verschiedenes. (Das, was man in Java »Variable« nennt,
> heißt in C++ »Objekt«.) Was soll dann also dann beispielsweise
> das »grundlegende Konzepte Variable« sein?
>
> Eine reine funktionale Sprache kennt weder solche Variablen
> noch Iteration, und Prolog sieht schon wieder anderes aus.
>
> Also würde ich eher sagen »Grundbegriffe imperativer Sprachen«
> oder »Grundbegriffe objektorientierter Sprachen« usw.

Du legst ja ganz schöne Maßstäbe an einen Text, den ich lediglich spontan
hingeschrieben habe. ;-)

Aber sei's drum. Du hast da natürlich Recht. Lass mich eine Analogie
bemühen, um zu verdeutlichen was ich mit "grundlegenden Konzepten der
Programmierung" meinte:

Bauklötze und was man damit anstellen kann, sind intuitiv (und im wahrsten
Sinn des Wortes) begreifbar. Um sich dann von Bauklötzen auf Lego oder
Fischer-Technik weiterzuentwickeln sind ebenfalls keine großen Hürden zu
überwinden, obwohl völlig neue Möglichkeiten wie feste Verbindungen,
Achsen, Gelenke, Bodenplatten etc. dazu kommen.

Beim Programmieren gibt es keine "Bauklötze" zum Einstieg. Selbst Versuche
in diese Richtung, wie Logo, zeigen schon eine erhebliche Komplexität.

Nun reicht es eben nicht, um im Bild zu bleiben, alle "Programm-Bauklötze"
einfach aus der Schachtel auf den Boden zu schütten und einfach darauf
zuzugreifen, sondern man muss auch noch bestimmte Regeln beachten wie
diese kombiniert werden können, um das erwartete Verhalten zu zeigen. Ein
Pointer beispielsweise könnte auf den ersten Blick mit einer "normalen"
Variablen verwechselt werden, ist dann aber doch etwas anderes.

Es gibt also eine Reihe verschiedenster Bauklötze, die Zusammengenommen,
dann ein Programm ergeben können. (Bei unsinniger, den Regeln nicht
folgender, oder gar zufälliger Zusammensetzung, aber doch nur Müll ergeben.)

Wenn ich also von "grundlegenden Konzepten der Programmierung" spreche,
meine ich, eine Beschreibung dieser Bauklötze mit ihren nicht sofort
offensichtlichen Eigenschaften.

Meine Kritik an modernen Büchern über Programmiersprachen ist dann, dass
diese einem lediglich noch zeigen, wie diese Bauklötze in der zu lernenden
Programmiersprache aussehen (=Syntax), aber ihre Eigenschaften
stillschweigend als Vorkenntnisse des Lesers voraussetzen.

Für einen in Informatik vorgebildeten Leser, der von oben auf die
Programmierung zugeht, mag das reichen.

Aber der nicht vorgebildete Leser, der Programmieren, quasi wie das
spielen mit Lego ausschließlich in der Praxis lernt, erfährt auf diese
Weise nicht genug, um am Ende wirklich programmieren zu können.

Oder um es wieder mal auf eine Auto-Analogie zu bringen: Jeder
Fahrzeugingenieur wird sicher auch Auto fahren können, aber nicht jeder
Autofahrer wird auch Fahrzeuge bauen können. Letzterer braucht aber
trotzdem ein wenig Theorie (Bremswege, Schwerpunktverlagerung bei
Zuladung, etc.) um sicher ans Ziel zu kommen.


Zu deinem Online Kurs: Ich freu mich schon, wenn ich ihn mal ohne das
schon besprochene Problem anschauen kann. Ich hoffe ich darf dann auch ein
wenig daran rumkritteln, bevor ich ihn weiterempfehle? ;-)

cu

Message has been deleted

Frank Buss

unread,
Jun 16, 2006, 2:15:11 PM6/16/06
to
Stefan Ram wrote:

> Kritik bin ich ja gewohnt und veröffentliche diese sogar
> selber in meinem Gästebuch, zwei Beispiele daraus:

Einiges an der Kritik scheint ja das Aussehen deiner Seiten zu betreffen
und deine unsäglichen Keyword-Listen, weswegen du ja auch scheinbar schon
aus Suchdiensten herausgeflogen bist. Falls du das Aussehen mal bei deinen
Seiten verbessern möchtest, dann empfehle ich http://www.oswd.org/ . Wenn
ich mal Zeit habe, werde ich wohl auch ein Layout davon für meine Webseiten
verwenden.

--
Frank Buss, f...@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de

Paul Ebermann

unread,
Jun 16, 2006, 6:53:57 PM6/16/06
to
"Stefan Ram" skribis:

> Deswegen behandle ich inzwischen in meinen Kursen
> zunächst ausführlich Aufrufe.
>
> Eine Übungsaufgabe in meinem Kurs lautet:
>
> java.lang
> Class Thread
> public static int activeCount()
> Returns the number of active threads in
> the current thread's thread group.
>
> Übersetzung der Beschreibung:
> Die Anzahl aktiver Verläufe in der
> Verlaufgruppe des aktuellen Verlaufs.
>
> Schreiben Sie ein Java-Programm, welches das Ergebnis
> eines Aufrufs des beschriebenen Wertdienstes ausgibt!
>
> Es gibt Kurse in denen (nach ausführlichen vorherigen
> Erklärungen dazu) von 8 Teilnehmern nach 10 Minuten
> nur 2 diese Aufgabe gelöst haben.

Ich vermute, selbst viele Leute hier in der Newsgroup
(sofern sie sich noch nicht an deine Ausdrucksweise
gewöhnt haben) haben mit dieser Aufgabenstellung Probleme.


Paul
--
Die Homepage von de.comp.lang.java: http://www.dclj.de
Pauls Package-Sammlung: http://purl.org/NET/ePaul/#pps

Bernd Eckenfels

unread,
Jun 16, 2006, 7:10:35 PM6/16/06
to
Paul Ebermann <Paul-E...@gmx.de> wrote:
>> Schreiben Sie ein Java-Programm, welches das Ergebnis
>> eines Aufrufs des beschriebenen Wertdienstes ausgibt!

Ob damit Thread.activeCount() gefragt ist? Ist das ne Übung zu statischen
MEthoden oder zu Threads? Im ersten Fall halte ich es für verwirrent die
Thread Klasse zu benutzen, im zweiten Fall halte ich die Aufgabenstellung
fuer etwas ungenau. Statt "rufen sie die methode auf" waere ja wohl
"ermitteln sie die anzahl der aktuellen verlaeufe in der verlaufgruppe des
hauptverlaufs" geschickter, oder?

>> Es gibt Kurse in denen (nach ausführlichen vorherigen
>> Erklärungen dazu) von 8 Teilnehmern nach 10 Minuten
>> nur 2 diese Aufgabe gelöst haben.
>
> Ich vermute, selbst viele Leute hier in der Newsgroup
> (sofern sie sich noch nicht an deine Ausdrucksweise
> gewöhnt haben) haben mit dieser Aufgabenstellung Probleme.

Mir tun die Leute in dem Kurs noch mehr Leid als die Leute hier in der
Gruppe... :-/ Ich glaub mit exzentrik kann man den Leuten den Spass an Java
übel verderben...

Gruss
Bernd

Sascha Schäfer

unread,
Jun 16, 2006, 7:17:27 PM6/16/06
to
> Dann bin ich froh, dass ich helfen konnte. Was ich von der Sinnhaftigkeit
> deines Codes halte, kannst du ja in unserem anderen Thread über Reflection
> nachlesen. ;-) Aber trotzdem, nur aus Interesse: Was fängst du denn mit
> der Liste der static final Member an?

Nur static Member, keine final! Die sammle ich, um statische Werte
vergleichen zu können. Du wirst dich sicher fragen, warum. Aber die
Erklärung würde den Rahmen sprengen und ich würde mir sicherlich auch wüste
Beschimpfungen anhören müssen.


mfg
Sascha


Message has been deleted
Message has been deleted
Message has been deleted

Ralf Ullrich

unread,
Jun 17, 2006, 2:18:44 PM6/17/06
to
Stefan Ram wrote:

>be-n...@lina.inka.de (Bernd Eckenfels) writes:
>>Ob damit Thread.activeCount() gefragt ist? Ist das ne Übung zu statischen
>>MEthoden oder zu Threads? Im ersten Fall halte ich es für verwirrent die
>>Thread Klasse zu benutzen,
>

> Es geht um den Aufruf parameterloser statischer Methoden
> mit primitiven Ergebnistypen.
>
> Welche wären denn besser als Beispiel geeignet?
>
> (Der Aufruf der Methode »java.lang.Math.random()« wurde zuvor
> bereits für Beispiele benutzt und kann daher nicht noch einmal
> als Übungsaufgabe gestellt werden.)

System.currentTimeMillis()

System.nanoTime()

Aber sehr viel mehr Auswahl hast du unter diesem Voraussetzungen kaum.

Du könntest dir ja mal schnell ein Programm schreiben, dass dir alle
Kandidaten (=static Methoden ohne Parameter mit primitive Return-Type) aus
rt.jar ausgibt, um noch mehr Ideen zu erhalten.

cu

Message has been deleted

Ralf Ullrich

unread,
Jun 17, 2006, 2:57:38 PM6/17/06
to
Stefan Ram wrote:

> Die Pfade aller Standard-Typen kriege ich ja notfalls aus dieser
> HTML-Quelle, indem ich »/« durch ».« ersetze.
>
>http://download.java.net/jdk6/docs/api/allclasses-frame.html
>
> Dann muß ich die Typen nur noch mit »getDeclaredMethods«
> durchsuchen. OK, das erscheint mir als machbar.

Ich würde einfach die rt.jar mit java.util.jar.JarFile öffen und alle
JarEntries mit entries() auslesen und durchgehen. Alle Entries, deren Name
auf .class endet, müßte man dann in einen Klassennamen umwandeln können,
den man mit Reflection in eine Class-Instanz wandeln kann. Evtl. kann man
zusätzlich noch diverse Packages wie sun.** und com.sun.** ausfiltern, und
non-public Klassen außen vor lassen.

cu

Ralf Ullrich

unread,
Jun 17, 2006, 3:32:04 PM6/17/06
to
Ralf Ullrich wrote:

Etwas so:

package ram;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class RuntimeJarReader {

static Set<String> s = new HashSet<String>();

public static void main(String[] args) throws IOException,
URISyntaxException, ClassNotFoundException {
// get URL of a class definitely in rt.jar
URL url = ClassLoader.getSystemResource("java/lang/Object.class");
// extract file URL
String jarPath = url.getPath();
URI uri = new URI(jarPath.substring(0, jarPath.indexOf('!')));

// open the Jar-File
JarFile jarFile = new JarFile(new File(uri));
for (JarEntry entry : Collections.list(jarFile.entries())) {
String name = entry.getName();
if (!name.endsWith(".class"))
continue;
if (name.startsWith("sun/"))
continue;
if (name.startsWith("sunw/"))
continue;
if (name.startsWith("com/sun/"))
continue;

String clzName = name.substring(0, name.length() - 6).replace('/',
'.');
Class clz = Class.forName(clzName);
if (clz.isAnonymousClass())
continue;
if (!Modifier.isPublic(clz.getModifiers()))
continue;
for (Method method : clz.getDeclaredMethods()) {
if (!Modifier.isPublic(method.getModifiers()))
continue;
if (!Modifier.isStatic(method.getModifiers()))
continue;
if (Modifier.isAbstract(method.getModifiers()))
continue;
if (method.getParameterTypes().length > 0)
continue;
if (!method.getReturnType().isPrimitive())
continue;
if (method.getReturnType() == Void.TYPE)
continue;
System.out.println(method);
}
}
}
}

Was dann

public static int java.awt.MouseInfo.getNumberOfButtons() throws
java.awt.HeadlessException
public static boolean java.rmi.dgc.VMID.isUnique()
public static int java.sql.DriverManager.getLoginTimeout()
public static boolean
javax.naming.spi.NamingManager.hasInitialContextFactoryBuilder()
public static int javax.swing.DebugGraphics.flashCount()
public static int javax.swing.DebugGraphics.flashTime()
public static int java.awt.dnd.DragSource.getDragThreshold()
public static boolean java.awt.dnd.DragSource.isDragImageSupported()
public static boolean java.beans.Beans.isDesignTime()
public static boolean java.beans.Beans.isGuiAvailable()
public static boolean java.net.HttpURLConnection.getFollowRedirects()
public static boolean javax.imageio.ImageIO.getUseCache()
public static boolean
javax.swing.JPopupMenu.getDefaultLightWeightPopupEnabled()
public static boolean javax.swing.Timer.getLogTimers()
public static boolean javax.swing.JDialog.isDefaultLookAndFeelDecorated()
public static boolean javax.swing.FocusManager.isFocusManagerEnabled()
public static boolean javax.swing.SwingUtilities.isEventDispatchThread()
public static boolean javax.swing.JFrame.isDefaultLookAndFeelDecorated()
public static long java.awt.EventQueue.getMostRecentEventTime()
public static boolean java.awt.EventQueue.isDispatchThread()
public static boolean java.awt.GraphicsEnvironment.isHeadless()
public static boolean
java.net.URLConnection.getDefaultAllowUserInteraction()
public static double java.lang.Math.random()
public static double java.lang.StrictMath.random()
public static int java.lang.Thread.activeCount()
public static boolean java.lang.Thread.interrupted()
public static native long java.lang.System.currentTimeMillis()
public static native long java.lang.System.nanoTime()

ausgibt, und damit zeigt, dass du die sinnvollen Kandidaten praktsich
schon ausgeschöpft hast.

cu

Message has been deleted

Bernd Eckenfels

unread,
Jun 17, 2006, 5:49:28 PM6/17/06
to
Stefan Ram <r...@zedat.fu-berlin.de> wrote:
> Da gab es ja auch immer einmal die Diskussionen über
> Speicherung von Programmen in Datenbanken mit
> Abfragemöglichkeit. Dein Programm zeigt, daß man mit dem
> Zugriff auf Klassendateien und dem Paket »java.lang.reflect«
> gar nicht so weit entfernt davon ist.

Im Oracle Magazin is grade nen Artikel bei dem sie eine XMI Datei in eine
XMLDB Datei importiert haben, und dann mittels queries suchen auf den
klassen und methoden zulassen.

Gruss
Bernd

Wanja Gayk

unread,
Jun 17, 2006, 6:07:27 PM6/17/06
to
ne...@jnana.de said...

> for (Iterator iter = children.iterator(); iter.hasNext();) {
> Object child = iter.next();
> if (invalid(child)) {
> continue;
> }
> if (!m_todo.contains(child) && !m_known.contains(child))
> m_todo.add(child);
> }

das würde bei mir übrigens (weil ich "continue" meistens deplatziert
finde) so aussehen:

for (Iterator iter = children.iterator(); iter.hasNext();) {
Object child = iter.next();
if (!(invalid(child) || m_todo.contains(child)
|| m_known.contains(child))){
m_todo.add(child);
}
}

Gruß,
-Wanja-

--
"Gewisse Schriftsteller sagen von ihren Werken immer: 'Mein Buch, mein
Kommentar, meine Geschichte'. [..] Es wäre besser, wenn sie sagten:
'unser Buch, unser Kommentar, unsere Geschichte'; wenn man bedenkt, dass
das Gute darin mehr von anderen ist als von ihnen." [Blaise Pascal]

Ralf Ullrich

unread,
Jun 17, 2006, 6:54:24 PM6/17/06
to
Wanja Gayk wrote:

>ne...@jnana.de said...

Hey, ich habe auch einen richtigen Namen!

>> for (Iterator iter = children.iterator(); iter.hasNext();) {
>> Object child = iter.next();
>> if (invalid(child)) {
>> continue;
>> }
>> if (!m_todo.contains(child) && !m_known.contains(child))
>> m_todo.add(child);
>> }
>
>das würde bei mir übrigens (weil ich "continue" meistens deplatziert
>finde) so aussehen:
>
>for (Iterator iter = children.iterator(); iter.hasNext();) {
> Object child = iter.next();
> if (!(invalid(child) || m_todo.contains(child)
> || m_known.contains(child))){
> m_todo.add(child);
> }
>}

Ja, ich versuche normalerweise continue und break auch zu vermeiden, weil
es den Schleifenablauf durcheinanderbringt, aber ich hasse dafür extra
lange If-Bedingungen umso mehr, und wenn ich diese durch ein if-continue
oder if-break verkleinern kann, finde ich das lesbarer.

Übrigens, bei dir stimmt die Bedingung nicht mehr. Aber das wäre dir
sicher aufgefallen, wenn du die If-Bedingung durch das entfernen des
continue nicht so unnötig lang und kompliziert gemacht hättest. Womit du
unfreiwillig, meine Argumentation bestätigt hast.

cu

Ralf Ullrich

unread,
Jun 17, 2006, 7:04:56 PM6/17/06
to
Stefan Ram wrote:

>"Ralf Ullrich" <ne...@jnana.de> writes:
>>URL url = ClassLoader.getSystemResource("java/lang/Object.class"); (...)
>>URI uri = new URI(jarPath.substring(0, jarPath.indexOf('!'))); (...)


>>JarFile jarFile = new JarFile(new File(uri));
>

> Vielen Dank! Da ich einige der obigen Aufrufe nicht kannte,
> hätte ich länger gebraucht. Ich hätte nämlich erst danach
> suchen müssen.

Jetzt wo du das nochmal besonders lobst, fällt mir auf, dass das
eigentlich nur ein billiger Hack ist, der mir aber offenbar geläufiger
ist, als das korrekte Vorgehen:

URL url = ClassLoader.getSystemResource("java/lang/Object.class");

JarURLConnection conn = (JarURLConnection) url.openConnection();
JarFile jarFile = conn.getJarFile();

Merk dir also besser dieses Vorgehen, damit es dir nicht so geht wie mir.

cu

Message has been deleted

Jan Thomä

unread,
Jun 18, 2006, 3:27:37 AM6/18/06
to
On 18 Jun 2006 04:13:45 GMT Stefan Ram wrote:

> ( final T[] iterable, final Consumer<T> consumer )
> { for( final T t : iterable )
> if( !consumer.consume( t )){ return false; }
> return true; }
> public static <T> boolean iterate
> ( final java.lang.Iterable<T> iterable, final Consumer<T> consumer )
> { for( final T t : iterable )
> if( !consumer.consume( t )){ return false; }
> return true; }

Urks, Du bist Dir aber wirklich sicher, daß Du nicht Deine Profession
verfehlt hast? Das hat irgendwie eher Ähnlichkeit mit Perl als mit Java...
Ich glaube wir sind nicht mehr im Zeitalter wo man Speicher sparen muß.
Mein früherer Professor hat in der Art Delphi programmiert - wir nannten
das "Programmieren im Blocksatz", jedes Byte zählt... :D
http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html - macht Deine
durchaus guten Programme zusätzlich auch noch gut lesbar - wenn das nichts
ist... ;).

Grüße,
Jan

Wanja Gayk

unread,
Jun 18, 2006, 6:28:23 AM6/18/06
to

Ralf Ullrich said...

> >ne...@jnana.de said...
>
> Hey, ich habe auch einen richtigen Namen!

Jau, aber mein Newsreader macht das nicht immer richtig, sorry.

> >> for (Iterator iter = children.iterator(); iter.hasNext();) {
> >> Object child = iter.next();
> >> if (invalid(child)) {
> >> continue;
> >> }
> >> if (!m_todo.contains(child) && !m_known.contains(child))
> >> m_todo.add(child);
> >> }
> >
> >das würde bei mir übrigens (weil ich "continue" meistens deplatziert
> >finde) so aussehen:
> >
> >for (Iterator iter = children.iterator(); iter.hasNext();) {
> > Object child = iter.next();
> > if (!(invalid(child) || m_todo.contains(child)
> > || m_known.contains(child))){
> > m_todo.add(child);
> > }
> >}

> Übrigens, bei dir stimmt die Bedingung nicht mehr.

Hö?

Original:


if (invalid(child)) {
continue;
}
if (!m_todo.contains(child) && !m_known.contains(child))
m_todo.add(child);

-> continue entfernen

if (!invalid(child)) {


if (!m_todo.contains(child) && !m_known.contains(child))
m_todo.add(child);
}

-> if durch && ersetzen

if (!invalid(child))
&& (!m_todo.contains(child) && !m_known.contains(child))
m_todo.add(child);
}

-> !A & !B = !(A|B) (De Morgan)

if (!invalid(child))
&& !(m_todo.contains(child) || m_known.contains(child))
m_todo.add(child);
}

-> !A & !B = !(A|B) (De Morgan)

if (!(invalid(child)
|| (m_todo.contains(child) || m_known.contains(child))){
m_todo.add(child);
}

-> a|(b|c) = a|b|c

if (!(invalid(child)
|| m_todo.contains(child) || m_known.contains(child)){
m_todo.add(child);
}

Habe ich da irgendwas übersehen?

Ralf Ullrich

unread,
Jun 18, 2006, 7:20:58 AM6/18/06
to
Stefan Ram wrote:

>/* "Ralf Ullrich" <ne...@jnana.de> writes:
>>Merk dir also besser dieses Vorgehen, damit es dir nicht so geht wie mir.
>

> OK. Meine Version Deines Codes sieht jetzt so aus:


>interface Filter
>{ boolean passed( java.util.jar.JarEntry entry );
> boolean passed( final java.lang.Class class_ );
> boolean passed( java.lang.reflect.Method method ); }

In der Standard Java API gibt es schon ein paar Filter, die IIRC alle
"accept" als Methodennamen benutzen. Daran würde ich mich an deiner Stelle
orientieren, auch wenn "accept" üblicherweise auch bei Source&Sink
verwendet wird.

Außerdem würde ich das Interface zu einem inneren Interface des Finder
machen, damit auch klar ist, wofür dieser Filter ist, denn man spricht es
dann im Code gewöhnlich als Finder.Filter an.

>class Util
...


> public static <T> boolean iterate

> ( final T[] iterable, final Consumer<T> consumer )

...


> public static <T> boolean iterate
> ( final java.lang.Iterable<T> iterable, final Consumer<T> consumer )

...

Hier würde ich noch

public static <T> boolean iterate

( final java.util.Iterator<T> iterator, final Consumer<T> consumer )


public static <T> boolean iterate

( final java.util.Enumeration<T> enumeration, final Consumer<T> consumer )

hinzufügen (evtl. als "iterateOnce"), damit hier

>class Finder
...
> public void inspectJar()
...
> Util.iterate
> ( java.util.Collections.list( jarFile.entries() ), inspectEntry ); }

Das zusätzliche verpacken als Liste unnötig wird, denn es findet hier nur
statt, um das JDK 1.0 Enumeration Interface zum JDK 1.2 Collections
Framework kompatibel zu machen. Wenn du ohnehin die foreach-Schleife
abstrahierst, könntest du Enumeration auch direkt unterstützen.

Weiteres ist mir auf die Schnelle nicht aufgefallen, und länger konnte ich
meinen Augen deine Codeformatierung nicht zumuten. ;-) (Du bist ja Kritik
gewöhnt.)

cu

Ralf Ullrich

unread,
Jun 18, 2006, 7:25:59 AM6/18/06
to
Wanja Gayk wrote:

>
>Ralf Ullrich said...


>
>
>>>> for (Iterator iter = children.iterator(); iter.hasNext();) {
>>>> Object child = iter.next();
>>>> if (invalid(child)) {
>>>> continue;
>>>> }
>>>> if (!m_todo.contains(child) && !m_known.contains(child))
>>>> m_todo.add(child);
>>>> }
>>>
>>>das würde bei mir übrigens (weil ich "continue" meistens deplatziert
>>>finde) so aussehen:
>>>
>>>for (Iterator iter = children.iterator(); iter.hasNext();) {
>>> Object child = iter.next();
>>> if (!(invalid(child) || m_todo.contains(child)
>>> || m_known.contains(child))){
>>> m_todo.add(child);
>>> }
>>>}
>
>>Übrigens, bei dir stimmt die Bedingung nicht mehr.
>
>Hö?
>
>Original:
>if (invalid(child)) {
> continue;
>}
>if (!m_todo.contains(child) && !m_known.contains(child))
>m_todo.add(child);
>
>-> continue entfernen

>-> if durch && ersetzen

>-> !A & !B = !(A|B) (De Morgan)
>-> !A & !B = !(A|B) (De Morgan)

>-> a|(b|c) = a|b|c
>
> if (!(invalid(child)
> || m_todo.contains(child) || m_known.contains(child)){
> m_todo.add(child);
> }
>
>Habe ich da irgendwas übersehen?

Nee, aber ich habe das fitzelchen (!( ganz am Anfang der Bedingung
übersehen. Du hast es also korrekt umgewandelt. Meine Argumentation wurde
trotzdem bestätigt. (Allerdings dann doch durch mich selbst ;P )

cu

Message has been deleted

Wanja Gayk

unread,
Jun 19, 2006, 4:51:05 AM6/19/06
to
ne...@jnana.de said...

> >Habe ich da irgendwas übersehen?
>
> Nee, aber ich habe das fitzelchen (!( ganz am Anfang der Bedingung
> übersehen. Du hast es also korrekt umgewandelt. Meine Argumentation wurde
> trotzdem bestätigt. (Allerdings dann doch durch mich selbst ;P )

Reine Gewöhnungssache :-)

Ingo R. Homann

unread,
Jul 3, 2006, 6:19:33 AM7/3/06
to
Hi Stefan!

Stefan Ram wrote:
> Ich berücksichtige Kritik ja manchmal auch:
>
> So ist derzeit der gesamte veröffentlichte Quelltext von
> »ram.jar« vor der Veröffentlichung automatisch in der eher von
> der Mehrheit dieser Runde bevorzugten Weise umformatiert
> worden.

Hey, das ist doch mal was! Wenn Du jetzt noch auf skurile
Wortschöpfungen wie "Wertdienst" (*) verzichten würdest und stattdessen
wie Du selbst erkannst hast, landläufig übliche Bezeichnungen wie
"Methoden" verwenden würdest, dann wären deine Beiträge tatsächlich
wieder sehr lohnenswert zu lesen (momentan lese ich sie ausschließlich
zum Amüsement :-)

Ciao,
Ingo

(*) Ist das das Gegenteil von "Ziviltdienst"? ;-)

Message has been deleted

Ingo R. Homann

unread,
Jul 4, 2006, 3:14:31 AM7/4/06
to
Hi Stefan,

Stefan Ram wrote:
> Wenn sie dich amüsieren, dann ist dies ja auch in einem
> Sinne lohnenswert!

Das stimmt. ;-) Sollte auch nicht böse gemeint sein, aber deine
"extravagante" Ausdrucksweise und Deine "ungewöhnliche" Codeformatierung
machen mir das Lesen deiner mails mittlerweile so schwer, dass ich es
gar nicht mehr versuche, sie zu verstehen (sondern einfach nur
Informatiker-mäßig meine kleine Freude über unverständliche
Wortschöpfungen wie "Gespinsterdienst" etc habe).

Und wenn ich mir diverse Kommentare anderer Leute auf deine Postings
ansehe, und bedenke, dass auf viele deiner Postings gar keiner
antwortet, drängt sich mir der Verdacht auf, dass fast alle es so sehen
wie ich.

Nur, weil dir die übliche Fachsprache (vielleicht sogar aus guten
Gründen) misfällt, macht es wenig Sinn, eine neue zu erfinden und zu
benutzen, wenn sie sonst niemand versteht oder gewillt ist zu lernen.
Und das wirst Du durch massiven Einsatz deiner Sprache auch nicht ändern
können, sondern im Gegenteil immer mehr Kommunikationsprobleme haben.
Das ist IMHO schade, denn an deiner Kompetenz in Bezug auf Informatik
habe ich keine Zweifel.

Ciao,
Ingo

Message has been deleted

Ingo R. Homann

unread,
Jul 4, 2006, 11:30:22 AM7/4/06
to
Hi,

Stefan Ram wrote:
>>Nur, weil dir die übliche Fachsprache (vielleicht sogar aus guten
>>Gründen) misfällt, macht es wenig Sinn, eine neue zu erfinden und zu
>>benutzen, wenn sie sonst niemand versteht oder gewillt ist zu lernen.
>

> Ich hatte ja in dem Artikel, auf den Du antwortest,...

Mir ging es mehr allgemein um deine mails (Zitat "Gespinsterdienst"),
nicht nur speziell um die, die der Auslöser war! (Vielleicht hätte ich
dafür einen neuen Thread aufmachen sollen? ;-)

Meinetwegen lasse ich mich trotzdem (ausnahmsweise ;-) auf eine
spezielle Diskussion ein:

> Operation: Bildet Vorzustand regelhaft auf Nachzustand ab

Akzeptiert.

> Wertoperation: Eine Operation, die einen Wert liefert
> (im Gegensatz zu einer Operation mit »void«-Ergebnis)

IMHO ist eine Unterscheidung dazwischen so selten explizit nötig, dass
es nicht lohnt, dafür einen Begriff zu (er)finden / suchen / sich auch
zu merken. Oder anders formuliert: Der Unterschied muss so selten
artikuliert werden dass man dann auch ruhig sagen kann "eine Methode,
die etwas zurückgibt (im Gegensatz zu einer Operation mit »void«-Ergebnis)".

(Sonst könnte man ja z.B. auch auf die Idee kommen, zwischen Methoden zu
unterscheiden, die einen nativen Typen oder stattdessen ein Objekt
(genauer: eine Referenz auf ein Objekt ;-) zurückgeben, oder speziell
welche, die z.B. einen String zurückgeben.)

> Dienst: Implementator einer Operation (z.B. ein Objekt;
> in Java aber auch eine Klasse [bei statischen Methoden])

Ich würde da auch wieder keinen eigenen Begriff schaffen sollen, sondern
im Zweifelsfall (falls so eine exakte Formulierung wirklich nötig ist)
von der "Klasse, die das Interface X implementiert" - oder eben schlicht
von "Klasse" - (bzw. "Objekt" bzw. "Referenz auf ein solches Objekt") reden.

> Methode: Implementation einer Operation in einem Dienst

"Methode" ist "Methode". Was soll da noch anders bezeichnet werden?
Spätestens an der Stelle sollte man echt einfach mal prakmatisch vorgehen.

Noch mal allgemeiner:

Um nicht missverstanden zu werden: Wenn Du in einer hochspezialisierten
Gruppe von Compiler-Bauern redest, mögen solche Begrifflichkeiten Sinn
machen. Aber als "normaler" Programmierer oder Software-Designer braucht
man das einfach nicht. Als Informatiker gewöhnt man sich - wenn man mit
DAUs spricht - doch auch ab, exakt zwischen "Usenet" und "Internet" oder
ähnlichen Begrifflichkeiten zu differenzieren, weil es dort keinen Sinn
macht und eine Klärung dieser Begriffe von der eigentlichen Diskussion
nur unnötig ablenkt. Mein Artzt sagt mir auch erstmal nur "sie haben
einen Knorpel im Knie" und erläutert fachliche Details erst auf genauere
Nachfragen hin, weil mir seine lateinischen Fachbegriffe sonst nichts
sagen. Natürlich sollte man immer soweit ins Detail gehen (und so exakt
sein) wie nötig, und meinetwegen auch ein wenig darüber hinaus, aber
wenn man es damit übertreibt, geht das ganz arg nach hinten los, wie Du
ja am eigenen Leib erfährst (Akademikerkrankheit (*)).

Ciao,
Ingo

(*) Ist nicht böse gemeint, ich bin ja selbst so einer! ;-)

Message has been deleted

Ingo R. Homann

unread,
Jul 5, 2006, 3:39:30 AM7/5/06
to
Hi,

Stefan Ram wrote:
>>>Wertoperation: Eine Operation, die einen Wert liefert
>>>(im Gegensatz zu einer Operation mit »void«-Ergebnis)
>>
>>IMHO ist eine Unterscheidung dazwischen so selten explizit nötig, dass
>

> Ich kenne viele Anfängerfehler der Art wie:
>
> java.lang.System.out.println( stringBuilder.setCharAt( 2, 'a' ));
>
> oder
>
> java.lang.Math.cos( 0 ); /* als eine vollständige Anweisung gemeint */
>
> Zur Erklärung, warum das so nicht sinnvoll ist, soll die
> Unterscheidung zwischen »Wertoperation« und »Wirkoperation«
> dienlich sein, die ja auch der Didaktiker Wirth in seiner
> Sprache Pascal durch die Unterscheidung zwischen »Function«
> und »Procedure« fixiert hat.

...die mittlerweile wieder fallen gelassen wurde bzw. von den meisten
(mir bekannten) Programmiersprachen nicht mehr genutzt wird!

Sicher ist es wichtig, dem Anfänger den Unterschied zu erklären, aber
wenn man das einmal getan hat, merkt auch der Anfänger, dass der
Unterschied so trivial ist, dass er sich da später keine Gedanken drum
machen muss, und deine Begrifflichkeiten nie wieder benötigt.

Davon abgesehen bräuchtest Du noch einen dritten Begriff, nämlich für
Methoden, die den Zustand des Objektes ändern UND etwas zurückgeben. ;-)

Um mal dein unten angeführtes Beispiel eines kleinen, lernenden Kindes
zu bemühen: Wenn es aus einer Trinkflasche nuckelen will, die leider
leer ist, und nichts raus kommt, bringst Du ihm auch nicht extra zwei
neue Worte bei (z.B. "Kuddelix" für "leere Flasche" und "Moddelart" für
"volle Flasche"), sondern erklärst ihm das einfach mit den Worten, die
schon dafür existieren und gebräuchlich sind (selbst, wenn sie etwas
länger sind), und die lauten nun mal ganz einfach "leere Flasche" und
"volle Flasche".

Sag mal, steht hier eigentlich irgendwo eine versteckte Kamera? ;-)

>>Ich würde da auch wieder keinen eigenen Begriff schaffen sollen, sondern
>>im Zweifelsfall (falls so eine exakte Formulierung wirklich nötig ist)
>>von der "Klasse, die das Interface X implementiert" - oder eben schlicht
>>von "Klasse" - (bzw. "Objekt" bzw. "Referenz auf ein solches Objekt") reden.
>
>

> Der Begriff »Dienst« soll mir auch dabei helfen, Polymorphie
> durch Vergleich mit Alltagsphänomen zu veranschaulichen:
>
> Wenn »paradiso« und »broadway« zwei verschiedenen
> Pizza-/Dienste/ sind, dann liefert
>
> paradiso.margherita()
>
> im allgemeinen etwas anderes als
>
> broadway.margherita()
>
> Das Wort »Dienst« kennt man aus der Alltagssprache dafür und
> kann es so auch auf die Programmierung übertragen.

Nun, hier hast Du ein Beispiel gewählt, in dem das zufällig gut passt.
Im allgemeinen (99% der Fälle) ist z.B. System.out.println() oder
Math.cos() oder InputStream.read() oder ... aber IMHO nichts, was
irgendwas mit dem zu tun hat, was man umgangssprachlich mit einem
"(Pizza-)Dienst" verbindet...

>>Als Informatiker gewöhnt man sich - wenn man mit DAUs spricht -
>>doch auch ab, exakt zwischen "Usenet" und "Internet" oder
>>ähnlichen Begrifflichkeiten zu differenzieren,
>

> Manchmal ist es aber auch richtig, dies gerade /nicht/ zu tun.

Unbestritten - ich habe allerdings hier schon viele Meinungen gelesen,
dass Du mit deiner Sprache weit übers Ziel hinaus schiesst, und noch
*keine einzige*, die dir beipflichten würde. Nun macht es natürlich
keinen Sinn, soetwas demokratisch abzustimmen, und es ist auch nicht so,
dass die Mehrheit immer Recht hat, aber wenn es um etwas wie Sprache
geht, macht es i.d.R. schon Sinn, sich auf die Mehrheit (innerhalb der
Gruppe, in der man gerade spricht) einzulassen.

> Kleine Kinder lernen ja auch kein richtiges Deutsch, wenn man
> mit Ihnen nur in »Babysprache« (was Erwachsene dafür halten)
> spricht, Nichtdeutsprachige lernen es auch nicht richtig, wenn
> Du ihnen den Weg mit »Du nix gehen links. Du gehen gerade,
> dann Du gehen rechts.« erklärst.

Full ACK. Daher spreche ich mit ihnen, wie die meisten Leute (in diesem
Umfeld) miteinander sprechen!

Ciao,
Ingo

Message has been deleted

Ralf Ullrich

unread,
Jul 5, 2006, 4:54:31 AM7/5/06
to
Stefan Ram wrote:

> Wirkoperationen entsprechen den Verbalphrasen der deutschen
> Sprache (z.B. »print(0)« ~ »Drucke 0!«) und die
> Wertoperationen den Nominalphrasen (z.B. »sin(0)« ~ »der Sinus
> von 0«). Aus Verbal- und Nominalphrasen baut man Sätze:
> »print(sin(0))« ~ »Drucke den Sinus von 0!«

Und was ist dann "formatiere":

int planet = 7;
String event = "a disturbance in the Force";
MessageFormat msg = new MessageFormat(
"At {1,time} on {1,date}, there was {2} on"
+ " planet {0,number,integer}.");

String result = msg.format(planet, new Date(), event);

Deiner Definition nach, müsste das einer Nominalphrase entsprechen, da ein
Wert zurückgeliefert wird. Die Verwendung eines Verbs deutet dagegen eher
auf eine Verbalphrase, aber auf den Zustand des msg Objekts wird ja gar
nicht eingwirkt.

cu

Message has been deleted

Ralf Ullrich

unread,
Jul 5, 2006, 5:22:19 AM7/5/06
to
Stefan Ram wrote:

>"Ralf Ullrich" <ne...@jnana.de> writes:
>>String result = msg.format(planet, new Date(), event);
>>Deiner Definition nach, müsste das einer Nominalphrase entsprechen,
>

> Ja:
>
> »Das Produkt der Formatierung des Tripels
> ( planet, new Date(), event ) durch das Objekt msg«


>
>>da ein Wert zurückgeliefert wird. Die Verwendung eines Verbs
>>deutet dagegen eher auf eine Verbalphrase, aber auf den Zustand
>>des msg Objekts wird ja gar nicht eingwirkt.
>

> Das Verb bezeichnet hier die Operation »formatieren«
> oder (wie in meiner obigen Nominalphrase) die »Formatierung«
> und muß noch mit dem in Java nicht explizit notiertem
> »das Produkt der ...« ergänzt werden, um die obige
> Nominalphrase zu erhalten.
>

Ich darf dich zitieren:

> Da es zum Umgang mit natürlichen Sprachen hilfreich ist, sich
> des Unterschieds zwischen Verbal- und Nominalphrasen bewußt zu
> sein, hoffe ich, daß dies auch beim Erlernen des Programmierens
> hilft.

Nun habe ich dir ein Beispiel gezeigt, wo diese, in deinen Augen
hilfreiche Analogie, ziemlich in die Hosen geht und nur durch in meinen
Augen erhebliche Verrenkungen zu retten ist.

Glaubst du nicht, dass es für den Lernenden somit eher verwirrender ist,
wenn du ihm von Nominal- und Verbphrasen erzählst, wenn er dann in der
Hälfte der Fälle doch mit dieser "Faustregel" daneben liegt?

Weitere spontane Beispiele:

StringBuilder#reverse()

ProcessBuilder#start()

URL#openStream()

Observer#update()

dazu noch so gut wie jeder Getter.


Wenn deine Motivation wirklich ist, Leuten den Einstieg zu erleichtern,
dann ist diese Analogie zur natürlichen Sprache dazu meiner Meinung nach
in höchstem Maße ungegeignet. Und mit ihr zusammen wahrscheinlich auch die
gesamte Unterscheidung in (Wirk-)(Wert-)Operation.

cu

Message has been deleted

Ingo R. Homann

unread,
Jul 5, 2006, 5:30:02 AM7/5/06
to
Hi,

Stefan Ram wrote:
> In Java gibt es tatsächlich keine spezielle Bezeichnung für
> Methoden mit oder ohne void-Ergebnis, aber der Unterschied ist
> doch an verschiedenen Stellen wichtig.

Wie ich schon schrieb, gibt es auch einen wichtigen Unterschied, ob eine
Flasche leer oder voll ist. Trotzdem erfindest Du dafür nicht
irgendwelche Wörter!

Ach was solls, ich gab's endgültig auf.

Ciao,
Ingo, resignierend

Message has been deleted

Ingo Menger

unread,
Jul 5, 2006, 9:00:17 AM7/5/06
to

Ralf Ullrich wrote:

> Nun habe ich dir ein Beispiel gezeigt, wo diese, in deinen Augen
> hilfreiche Analogie, ziemlich in die Hosen geht und nur durch in meinen
> Augen erhebliche Verrenkungen zu retten ist.
>
> Glaubst du nicht, dass es für den Lernenden somit eher verwirrender ist,
> wenn du ihm von Nominal- und Verbphrasen erzählst, wenn er dann in der
> Hälfte der Fälle doch mit dieser "Faustregel" daneben liegt?

Ich muß mal eine Lanze für Stefan brechen. Zwar finde ich die
Ausdrücke Wirk- und Wertoperation reichlich, wie soll ich sagen,
kauzig? Ich sehe keinen Grund, die etablierten Begriffe "Funktion" und
"Prozedur" nicht zu verwenden.

Ich glaube jedoch, man kann kaum überschätzen, wie wichtig der
Unterschied ist.
Die "Übersetzung" in natürliche Sprache kann da hilfreich sein. Zum
Beispiel läßt sich so ohne weiteres verstehen, warum
Math.cos(0);
oder
mycar.colour();
für sich allein genauso sinnvoll sind wie
"der Cosinus von 0"
oder
"die Farbe meines Autos"
als Satz gedacht. Sehr wohl sinnvoll jedoch als Antwort auf eine Frage
wie "Welche Farbe hat Stefans Auto?"

> Weitere spontane Beispiele:
> StringBuilder#reverse()

"the reverse" gibt es (das Spiegelbild, das Umgedrehte), oder nicht?

> ProcessBuilder#start()

Das ist tatsächlich eine Prozedur, oder?

> URL#openStream()

Ebenso. Aber man kann das natürlich auch als Funktion sehen, quasi als
gebe es für manche URL einen offenen Stream ("open stream" (!) in
english) und die Funktion sei definiert als
offenerStrom url = der zugehörige offene Strom, falls existent,
andernfalls null
English ist insofern eine zweideutige Sprache, weil ohne Kontext oft
nicht klar ist, ob etwas (z.B. "open" in "open door") ein Verb oder ain
Adverb ist. Es steht aber, wie gezeigt, auch der Interpretation als
Adverb nichts im Wege, wenn man nicht ein Modell der Programmiersprache
im Kopf hat, wo alles und jedes immer irgendetwas *tut* (imperativ),
sondern wo einiges auch einfach nur etwas *ist* (deklarativ). Die
Übersetzung von
final double x = Math.sqrt(2.0);
heißt bei mir jedenfalls
"Sei x die Wurzel aus 2"
und nicht
"berechne die Wurzel aus 2 und speichere das Ergebnis in x"
(Im übrigen wäre ich dafür, daß final Standard ist und man
umgekehrt nonfinal oder mutable oder volatile definieren muß, wenn
man's braucht.)

> Observer#update()

auch eine Prozedur. Wie auch immer, sowohl die Übersetzung
"aktualisiere" als auch "die Aktualisierung" ist denkbar.

> dazu noch so gut wie jeder Getter.

Das allerdings nur, wenn man Getter nach dem Schema getX() benennt, was
freilich unglücklich ist (sich aber wahrscheinlich nicht mehr ändern
läßt). Weniger imperativ gedacht, tut ein Getter nicht was (er holt
mir den Wert des Attributes X), sondern y.getX() *ist* "das Attribut X
des Objekts y" und sollte daher eigentlich theX heißen.


>
>
> Wenn deine Motivation wirklich ist, Leuten den Einstieg zu erleichtern,
> dann ist diese Analogie zur natürlichen Sprache dazu meiner Meinung nach
> in höchstem Maße ungegeignet.

Muß nicht, wie ich hoffe gezeigt zu haben. Nur in Verbindung mit der
Ersetzung gebräuchlicher Anglizismen und Fachbegriffe durch
gekünstelt wirkende Eindeutschungen wird es IMHO obskur.

> Und mit ihr zusammen wahrscheinlich auch die
> gesamte Unterscheidung in (Wirk-)(Wert-)Operation.

Das allerdings wäre schade. Im übrigen bin ich bei der
Dienst-Terminologie nicht bei Stefan, denn da eine Methode (in erster
Näherung jedenfalls) nichts anderes ist als eine Funktion oder
Prozedur, die ein implizites Argument erhält, ist gar nicht
einzusehen, warum man die Dinge da verkomplizieren muß. Aus
alberto.pizza()
und "die Pizza des alberto Dienstes" wird so einfach "albertos pizza".

Ralf Ullrich

unread,
Jul 5, 2006, 9:46:34 AM7/5/06
to
Ingo Menger wrote:

>>Weitere spontane Beispiele:
>> StringBuilder#reverse()
>
>"the reverse" gibt es (das Spiegelbild, das Umgedrehte), oder nicht?

Ja, es ist hier aber wirklich eine "Wirkoperation", die jedoch auch
gleichzeitig einen Wert zurückliefert (this).

>> ProcessBuilder#start()
>
>Das ist tatsächlich eine Prozedur, oder?

Nein. Es wird ein Process zurückgegeben, der nach dem Zustand des
ProcessBuilder produziert wurde. Also eigentlich eine Wertoperation, die
aber über ein Verb aufgerufen wurde. (Und ja, start könnte auch ein Nomen
sein, aber dass es das in diesem Fall ist, wirst du hoffentlich nicht
ernsthaft behaupten wollen.) Bei anderen Buildern/Factories heißt eine
vergleichbare Methode meist newInstance() was wieder zu Stefans
Phrasen-Definitionen passen würde, oder aber createSomething(), was dann
wieder nicht passt.

>> Observer#update()
>
>auch eine Prozedur. Wie auch immer, sowohl die Übersetzung
>"aktualisiere" als auch "die Aktualisierung" ist denkbar.

Genau. Aber wenn es ein Nomen (Aktualisierung) ist, warum gibt es dann
keinen Wert zurück? Müsste es doch nach Stefans Faustregel.

Und wenn es ein Verb (aktualisiere) ist, was ist dann die regelhafte
Abbildung von Vor- auf Nachzustand, die es bewirken soll?


Noch ein schönes Beispiel wo Stefans Analogie versagt:

Object XPathExpression#evaluate(Object,QName)

a) Verb ergo sollte es eine Wirkoperation sein, aber es hinterlässt keine
Wirkung, der Zustand sowohl der XPathExpression, als auch der Argumente
bleibt unverändert.

b) Liefert einen Wert, sollte also eigentlich eine Nominalphrase sein.

Aber egal, was ich sagen wollte habe ich längst gesagt. Ich kann nur
hoffen, dass es bei Stefan Wirkung hinterlässt. Wenn nicht, ist es mir im
Prinip auch gleich, denn ich werde sicher niemals das zweifelhafte
Vergnügen haben, bei ihm Programmieren zu lernen. Ich muss mir nur
überlegen, ob ich seine Online-Seiten an Neulinge weiterempfehlen kann
oder nicht. Und im Moment ist seine Sprachextravaganz für mich ein starker
Grund es nicht zu tun.

cu

Sascha Broich

unread,
Jul 5, 2006, 9:54:18 AM7/5/06
to
On 5 Jul 2006 06:00:17 -0700, Ingo Menger wrote:

> Ralf Ullrich wrote:
>
>> dazu noch so gut wie jeder Getter.
>
> Das allerdings nur, wenn man Getter nach dem Schema getX() benennt, was
> freilich unglücklich ist (sich aber wahrscheinlich nicht mehr ändern
> läßt). Weniger imperativ gedacht, tut ein Getter nicht was (er holt
> mir den Wert des Attributes X), sondern y.getX() *ist* "das Attribut X
> des Objekts y" und sollte daher eigentlich theX heißen.

Naja, ich kann ja auch die Eigenschaften z.B. als Map implementieren und
dann mach getX() doch was, nämlich die Eigenschaft aus der Map holen.

Und setX() muss ja nicht nur zuweisen, sondern kann ja auch bei einer
Änderung des Wertes entsprechende Listener benachrichtigen.

Nur weil get und set oft als einfache Rückgabe bzw. Zuweisung implementiert
sind, muss das noch lange nicht heißen, dass sie nix machen.


Sascha Broich
--
Es ist nicht schlimm, alt auszusehen,
solange man älter ist, als man aussieht.

Ingo Menger

unread,
Jul 5, 2006, 12:19:28 PM7/5/06
to

Sascha Broich wrote:
> On 5 Jul 2006 06:00:17 -0700, Ingo Menger wrote:
>
> > Ralf Ullrich wrote:
> >
> >> dazu noch so gut wie jeder Getter.
> >
> > Das allerdings nur, wenn man Getter nach dem Schema getX() benennt, was
> > freilich unglücklich ist (sich aber wahrscheinlich nicht mehr ändern
> > läßt). Weniger imperativ gedacht, tut ein Getter nicht was (er holt
> > mir den Wert des Attributes X), sondern y.getX() *ist* "das Attribut X
> > des Objekts y" und sollte daher eigentlich theX heißen.
>
> Naja, ich kann ja auch die Eigenschaften z.B. als Map implementieren und
> dann mach getX() doch was, nämlich die Eigenschaft aus der Map holen.

Das ändert nichts, denn die Map kann auch aufgefaßt werden als eine
Funktion, die Strings auf Werte mappt.

> Und setX() muss ja nicht nur zuweisen, sondern kann ja auch bei einer
> Änderung des Wertes entsprechende Listener benachrichtigen.

Setter wären ja Prozeduren. Die machen in der Tat was.

> Nur weil get und set oft als einfache Rückgabe bzw. Zuweisung implementiert
> sind, muss das noch lange nicht heißen, dass sie nix machen.

Wie gesagt, setter machen was, da sie Prozeduren sind oder als solche
aufgefaßt werden können. (Ordentliche) Getter hingegen "tun" in genau
dem Sinne nichts wie Math.sqrt(2.0). D.h., Du könntest in einem
Method-Body vor jeder Anweisung Math.sqrt(2.0); einsetzen, ohne daß
das Verhalten des Programms erkennbar anders wird. Im Kontrast dazu
stell Dir vor, Du würdest vor jede Anweisung irgendeinen Setter
einsetzen.

Ingo Menger

unread,
Jul 5, 2006, 12:51:46 PM7/5/06
to

Ralf Ullrich wrote:
> Ingo Menger wrote:

> Noch ein schönes Beispiel wo Stefans Analogie versagt:
>
> Object XPathExpression#evaluate(Object,QName)
>
> a) Verb ergo sollte es eine Wirkoperation sein, aber es hinterlässt keine
> Wirkung, der Zustand sowohl der XPathExpression, als auch der Argumente
> bleibt unverändert.
>
> b) Liefert einen Wert, sollte also eigentlich eine Nominalphrase sein.

Das stimmt, aber man kann sich auf den Standpunkt stellen, daß die
Methode dann halt inkonsistent bzw. irreführend benamst ist. Sie
könnte z.B. result heißen.

Also, das ist völlig klar: nicht jeder tatsächlich benutzte
Methodenname paßt in Stefan's Schema. Außerdem trifft Stefan auch
m.E. nicht den wichtigen Punkt, nämlich ob die Funktion den Zustand
*beobachtbar* ändert.

Ich sehe es so:
- Alles sind Funktionen, void ist nur der Java-Slang für den
einelemtigen Typ, mit dessen Wert allerdings (in Java) nichts gemacht
werden kann. (sog unit Typ).
- Reine Funktionen sind solche, die den Zustand nicht bzw. zumindest
nicht erkennbar ändern. Solche wenn immer möglich zu verwenden hat
verschiedenste Vorteile.
- Umgekehrt sollte man bestrebt sein, die Ausführung von
Seiteneffekten und die Berechnung von Werten nicht zu verquicken.

Also nicht "Genau dann wenn void zurückgeliefert wird, ist es eine
Prozedur" sondern "Wenn dat Ding (beobachtbare) Seiteneffekte hat,
sollte es void liefern."

Wie auch immer, wenn man diese Trennung in "reine Funktionen" und
"Prozeduren" macht, dann bietet es sich auch an, eine Namenskonvention
zu verwenden, die erkennen läßt, was was ist. Und da wäre die Idee
"Nominal bzw. Adverbialkonstrukt" einerseits und "Imperativ eines
Verbs" andererseits nicht die schlechteste Konvention.

Message has been deleted
Message has been deleted

Ingo Menger

unread,
Jul 6, 2006, 4:28:58 AM7/6/06
to

Stefan Ram wrote:
> "Ralf Ullrich" <ne...@jnana.de> writes:
> >Ja, es ist hier aber wirklich eine "Wirkoperation", die jedoch auch
> >gleichzeitig einen Wert zurückliefert (this).
>
> Zu solchen Wirkwertoperationen gibt es, soweit ich mir bewußt
> bin, keine Entsprechung im Deutschen. Entweder etwas
> bezeichnet eine Handlung (Wirkoperation) oder ein Objekt
> (Wert).
>
> Diesen Unterschied kann man in zweifacher Weise deuten:
>
> Man könnte in ihm eine Hinweise darauf sehen, daß man dies
> auch beim Programmieren stets zu trennen habe.

So sehe ich das.

> Also nicht (in C):
>
> if( fopen( "tmp.txt", "r" ))/* ... */
>
> sondern (in Java):
>
> file.tryToOpen( "tmp.txt", "r" ); // Wirkoperation
> if( file.wasOpenedSuccessfully() )/* ... */ // Wertoperation

Man muß allerdings vernünftig abstrahieren. "fopen" operiert auf
einem abstrakten Datentyp FILE und liefert eine Referenz (i.e. in C
einen Zeiger). Dazu kommt noch: diese Filestruktur ist sowieso
irgendwie mehr "Selbstzweck". Wozu brauche ich die? Wenn ich eine
Funktion (Wertoperation) wie
List<String> theLinesOfFile(Path)
zur Verfügung hätte, bzw. ein entsprechendes Pedant in C, dann würde
ich diesen fopen-Kram bei der Eingabe nie machen. Wie auch immer, man
kann fopen(fn, "r") sehr wohl als Wertoperation sehen "die
Datenstruktur, die das Laufzeitsystem braucht, um fn einzulesen"
definiert als
einen Pointer auf diese Struktur, falls sie existiert, sonst
NULL
oder genauer
wenn ein FILE-Objekt berechnet werden kann, über das fn lesbar
wird, dann die entsprechende Referenz, sonst null.

Das Gedankenexperiment, den Code mit Aufrufen von fopen(..., "r")
anzureichern, ohne beobachtbare Auswirkungen zu erzielen, bestätigt
diese Einordnung. (Nicht so bei fopen(..., "w"), freilich.)

Dasselbe gilt für Java. Man kann sich vorstellen, daß es eine
virtuelle Map gibt, die Strings (bzw. genereller: Pfadnamen) auf Listen
von Bytes, Chars, Strings etc. abbildet. Tatsächlich ist das
Filesystem genau eine solche Map. Das Aufsuchen eines Eintrags einer
Map kann man aber nicht als Wirkoperation zählen. Dann wird die
Terminologie unglaubwürdig.

Ralf Ullrich

unread,
Jul 6, 2006, 5:20:56 AM7/6/06
to
Ingo Menger wrote:

>Das Gedankenexperiment, den Code mit Aufrufen von fopen(..., "r")
>anzureichern, ohne beobachtbare Auswirkungen zu erzielen, bestätigt
>diese Einordnung. (Nicht so bei fopen(..., "w"), freilich.)
>
>Dasselbe gilt für Java. Man kann sich vorstellen, daß es eine
>virtuelle Map gibt, die Strings (bzw. genereller: Pfadnamen) auf Listen
>von Bytes, Chars, Strings etc. abbildet. Tatsächlich ist das
>Filesystem genau eine solche Map. Das Aufsuchen eines Eintrags einer
>Map kann man aber nicht als Wirkoperation zählen. Dann wird die
>Terminologie unglaubwürdig.

Hier irrst du:

fopen(..., "r") hinterlässt sehr wohl beobachtbare Auswirkungen, zum
Beispiel im last access Attribut der Verzeichnisstruktur. Bei einem
Filesystem, das teilweise auf Offline Medien (z.B. Tapes) liegt, sind die
beobachtbaren Auswirkungen sogar noch umfangreicher. Bei Filesystemen wie
Windows sie verwendet, kommt dann möglicherweise auch noch das exklusive
Lock bis zum Aufruf von fclose dazu.

Das bedeutet letztlich, dass du zwar einen solchen Adaper schreiben könntest

FileSystemAdapter implements Map<File, ByteBuffer> {
FileSystem adaptee;
...
ByteBuffer get(File file) {
RandomAccessFile raf = new RandomAccessFile(file, mode);
FileChannel ch = raf.getChannel();
return ch.map(mapmode, 0, ch.size());
}
...
}

und dann ein FileSystem wie eine Map benutzen könntest. Aber dabei würden
die im Filesystem beobachtbaren Auswirkungen eines Map#get(...) via
FileSystemAdapater lediglich über das Map-Interface nicht beobachtbar
sein. Insofern hast du damit noch eine weitere Art von Operationen
geschaffen: die Wertoperation mit Wirkung auf verdeckte Operanden.

Da man das beliebig weiter treiben kann und so die Grenzen zwischen
wirkenden und nicht wirkenden Operationen immer mehr verschwimmen würden,
bleibe ich bei meiner Meinung, dass diese Art der Unterscheidung eben doch
nicht viel bringt. Man sollte einfach beim Begriff Methoden bleiben und
sie bezüglich ihrer Wirkung* und ihrer Rückgabe* individuell betrachten,
statt zu versuchen künstliche Oberbegriffe einzuführen, die in der
verwendeten Programmiersprache kein Pendant kennen, und daher immer nur
eine Annäherung sein können, die in vielen Fällen nicht passt.

cu

*) Und eine mögliche Wirkung kann eben auch keine Wirkung (=NOP) sein,
ebenso kann eine mögliche Rückgabe schlicht leer (=void) sein.

Message has been deleted

Ingo Menger

unread,
Jul 6, 2006, 7:02:21 AM7/6/06
to
Ralf Ullrich wrote:
> Ingo Menger wrote:
>
> >Das Gedankenexperiment, den Code mit Aufrufen von fopen(..., "r")
> >anzureichern, ohne beobachtbare Auswirkungen zu erzielen, bestätigt
> >diese Einordnung. (Nicht so bei fopen(..., "w"), freilich.)
> >
> >Dasselbe gilt für Java. Man kann sich vorstellen, daß es eine
> >virtuelle Map gibt, die Strings (bzw. genereller: Pfadnamen) auf Listen
> >von Bytes, Chars, Strings etc. abbildet. Tatsächlich ist das
> >Filesystem genau eine solche Map. Das Aufsuchen eines Eintrags einer
> >Map kann man aber nicht als Wirkoperation zählen. Dann wird die
> >Terminologie unglaubwürdig.
>
> Hier irrst du:
>
> fopen(..., "r") hinterlässt sehr wohl beobachtbare Auswirkungen, zum
> Beispiel im last access Attribut der Verzeichnisstruktur. Bei einem
> Filesystem, das teilweise auf Offline Medien (z.B. Tapes) liegt, sind die
> beobachtbaren Auswirkungen sogar noch umfangreicher. Bei Filesystemen wie
> Windows sie verwendet, kommt dann möglicherweise auch noch das exklusive
> Lock bis zum Aufruf von fclose dazu.

Ich versuche zu abstrahieren: die abstrakte Maschine, auf der mein
abstrakt gedachtes Programm läuft, hat unbegrenzten Speicher und keine
willkürlichen Limits wie NFILES (maximale Zahl der geöffneten Files).
Und nein, fopen spezifiziert nicht, daß die "Wirkung" ist, daß
irgendwelche Zugriffszeiten angepaßt werden (read only FS unter UNIX
und FAT32 existieren) oder daß das File gesperrt würde (UNIX).
Insofern ist dies keine "Wirkung" von fopen(), sondern ein für unsere
Betrachtung irrelevantes Feature der imperfekten, realen Maschine samt
Betriebssystem.


> und dann ein FileSystem wie eine Map benutzen könntest. Aber dabei würden
> die im Filesystem beobachtbaren Auswirkungen eines Map#get(...) via
> FileSystemAdapater lediglich über das Map-Interface nicht beobachtbar
> sein.

So kann man, glaiube ich, nicht argumentieren, wenn man über Konzepte
(Wirk- und Wertoperation) spricht. Dann kannst Du das ganze Thema damit
erschlagen, daß Du sagst, im Debugger kann ich ja beobachten, daß
sqrt(2) aufgerufen wird, also ist es eine Wirkoperation, denn sie
veranlaßt meinen Debugger, den Bildschirm anders zu beschreiebn als er
vorher war. :)

> Da man das beliebig weiter treiben kann und so die Grenzen zwischen
> wirkenden und nicht wirkenden Operationen immer mehr verschwimmen würden,
> bleibe ich bei meiner Meinung, dass diese Art der Unterscheidung eben doch
> nicht viel bringt.

Ich respektiere natürlich Deine Meinung, weise aber darauf hin, daß
das "verschwimmen" eine Sache der Abstraktion ist.
Nimm mal folgendes hypothetische Beispiel:
BigInt p = new BigInt("10").exp(new BigInt("100")); // berechnet
10^100
Natürlich wissen wir, daß dieser Code in der Tat einiges "tut". Es
werden Objekte auf dem Heap plaziert usw. Wenn das Heaplimit eng genug
ist, kann ein Programm ohne diese Zeile womöglich durchlaufen,
während es mit dieser Zeile wegen Speichermangel abstürzt -
sicherlich ein beobachtbarer Unterschied im Verhalten.
Würdest Du sagen, daß der Hersteller der BigInt-Klasse dies
dokumentieren muß?
Das macht kein Mensch, weil beim Leser der Dokumentation vorausgesetzt
wird, daß er weiß, daß a) "shit happens" und b) die Dokumentation
sich auf einem gewissen Abstraktionsniveau bewegt. Man schreibt:
"b.exp(k) berechnet die k-te Potenz von b für nichtnegative k" und
nicht "Falls mindestens noch soundsoviel Speicher frei ist oder durch
den Garbage-Kollektor freigemacht werden kann, berechnet b.exp(k) ..."
Daher ist - wenn man die Dokumentation als Maßstab nimmt - BigInt.exp
eine Wertoperation. Daß wir von den möglicherweise häßlichen
Interna der Klasse BigInt sowie von der Arbeitsweise der JVM in
gewissem Maße abstrahieren können und dürfen, finde ich eine der
wirklich wichtigen Leistungen von Programmiersprachen wie Java.

> Man sollte einfach beim Begriff Methoden bleiben und
> sie bezüglich ihrer Wirkung* und ihrer Rückgabe* individuell betrachten,
> statt zu versuchen künstliche Oberbegriffe einzuführen, die in der
> verwendeten Programmiersprache kein Pendant kennen, und daher immer nur
> eine Annäherung sein können, die in vielen Fällen nicht passt.

Ja, das muß man, wenn man existierenden Code verwendet sowieso tun.
Mir geht es eher um den Entwurf eigenen Codes. Und da bin ich durch
viel Leid zu der Auffassung gekommen, daß ein mehr "funktionaler" Stil
oft große Vorteile hat. Es ist eben schon ein Unterschied, wenn man
Point entwirft, ob man an sowas denkt:

class Point {
private int x, y;
Point(int ix, int iy) { x = ix; y = iy; }
void move(int dx, int dy) { this.x += dx; this.y += dy; }
}

oder eher an sowas:

class Point {
final private int x,y;
Point(int ix, int iy) { x = ix; y = iy; }
Point move(int dx, int dy) { return new Point(x+dx, y+dy); }
}

und dies für alle Klassen, wo es nicht unabweisbar anders, nämlich
mutabel sein muß, ähnlich macht.
Ob move() eine Wert- oder eine Wirkoperation wird, ist dann nicht egal
sondern eine wichtige Designfrage.
Auch wenn man persönlich diesen funktionalen Stil nicht mag, muß man
anerkennen, daß dem so ist.

Message has been deleted
Message has been deleted

Ingo Menger

unread,
Jul 7, 2006, 5:33:12 AM7/7/06
to

Stefan Ram wrote:
> r...@zedat.fu-berlin.de (Stefan Ram) writes:
> >»void« ist aber eben nicht einmal »null«, so wie man in der
> >Steuererklärung in den vereinigten Staaten auch
> >unterschiedliche Angaben macht, je nachdem, ob man in ein Feld
> >»0« oder »void« schreibt.
>
> Ein Beispiel dazu:
>
> Das Gewitter scheint einen Temperatursensor in
> Mitleidenschaft gezogen zu haben:
>
> Nun steht hier seit einigen Stunden:
>
> »Die Temperatur beträgt 0.0 °C.«
>
> http://www.met.fu-berlin.de/de/wetter/wetterbeobachtung/
>
> Die Darstellung eines unbekannten Wertes durch
> »0.0« kann hier nicht von der Darstellung des
> Wertes »0.0« unterschieden werden.
>
> Damit erzähle ich hier niemandem etwas Neues,
> es ist mir nur gerade aufgefallen, als ich die
> Temperatur lesen wollte.
>
> Man spricht auch manchmal von »in-band«- und
> »out-of-band«-Werten, wobei ich jetzt nicht genau
> weiß, wann.

Man macht das, wenn man eigentlich nur einen Subtype des von der
Programmiersprache unterstützten Types benötigt (die in-band Werte),
und die bzw. einen der restlichen out-of-band Werte dann dazu benutzt,
bestimmte Bedingungen zu indizieren.
In Deinem Temperaturbeispiel könnte man sich vorstellen, daß die
Temperatur in Zehntelgrad gemessen und als ganzzahliger Wert
abgespeichert wird. Zur Anzeige kommt dieser Wert als Dezimalzahl mit
einer Nachkommastelle.
Jetzt ist es so, daß man eigentlich nur im Bereich -2734..max
arbeitet, wobei max die größte darstellbare Ganzzahl sei. (Denn
kälter als 273.4°C kann es nicht werden.)
Also gehören alle Werte kleiner als -2734 nicht mehr zu den
Temperaturwerten. Man kann dann definieren "-2735 bedeutet: keine
Messung".

Message has been deleted
Message has been deleted
Message has been deleted
0 new messages