Laden...

Forenbeiträge von Traumzauberbaum Ingesamt 512 Beiträge

31.05.2006 - 15:50 Uhr

Naja zwei Bits gleich oder verschieden sind, bekommt man mit der XOR funktion.
Dort bekommt man aber nicht die Anzahl der Unterschiede, sondern eben eine 0 wenn es gleich ist, und eine 1 wenn es Unterschiedlich ist. Man müsste dann also die 1en Zählen.
Bringt imo garnichts an performance, ob ich nun die 2 Arrays durchgehe und per == vergleiche, oder erstmal in Integer umwandel, XOR ausführe, und dann jede Stelle durchgehe und die 1en zähle.

Also ist der einfacheren Variante der Vorzug zu geben 😉

@tano

Wenn ich dich richtig verstehe, ist dein einziges Problem, wie du ein Array mit dynamischer Größe benutzt.

Nimm dazu einfach eine ArrayList.
Du speicherst mit Add eine Zeile rein, und gehst dann mit foreach(...) alle Zeilen die drin sind durch.

30.05.2006 - 16:58 Uhr

Ich glaube nicht, dass sich da was ändern wird. Man kann sonst nur noch typeof(...) benutzen.

Ich hab etwas Ähnliches zum Lesen/Schreiben von Datein mit (Text-)Werten an fest definierten Stellen mit fest definierten Längen gemacht.

Dazu habe ich ein Attribut als Basis erstellt, bei dem man unter anderem ein (string) Format setzen kann, und das eine virtuellen Getter FormatProvider hat.

Die Funktion zum umwandeln eines Objekts in Text sieht dann (zum Teil) so aus:

if( !string.IsNullOrEmpty(attribute.Format) )
{
	valueString = string.Format( attribute.FormatProvider, "{0:"+attribute.Format+"}", data );
}
else if( data is IConvertible )
{
	valueString = ((IConvertible)data).ToString( attribute.FormatProvider );
}
else
{
	valueString = data.ToString();
}

in der Basisklasse liefert der Getter von FormatProvider null.
Wenn ich zum Beispiel für numerische Felder das Dezimaltrennzeichen ändern will, habe ich dafür ein von dem Attribut abgeleitetes Attribut, das immer einen NumericFormatProvider zurückliefert, und bei dem man dann die entsprechend wichtigen Properties setzen kann.

25.05.2006 - 19:14 Uhr

Ich benutze die Observer Pattern beim Remoting.

Das über Events zu machen hat einfach zu viele Probleme aufgeworfen.

25.05.2006 - 11:07 Uhr

Das erste Element in einem C# Array hat den Index 0. Du fängst immer mit 1 an zu zählen, und verpasst damit immer einen. Außerdem versteh ich nicht, warum du negGrad mit Länge 20 erzeugst. Das ist ja der Eingangsgrad der Knoten, also warum davon nicht genau soviele wie du Knoten hast?

Eine übliche Form duch alle Elemente in einem Array zu iterieren ist:

for( int i = 0; i < arrayName.Length; i++ )

Du hast dir echt Mühe gegeben die Nachteile von Pascal auf C# zu übertragen 😉

Außerdem kann diese Zeile einfach mal nicht stimmen, auch wenn ich nicht verstehe was pn ist:

for (j = pn[i]; j <= pn[i] - 1; j++)

Mathematisch betrachtet:
j = pn_ > pn_ - 1
Die schleife wird niemals ausgeführt, weil immer j > pn_ - 1 ist

23.05.2006 - 00:09 Uhr

Endians bei Textausgabe ist auch nicht wirklich etwas, das man braucht (im Gegensatz zu byte-Konvertierungen).

22.05.2006 - 19:42 Uhr

Namespaces können nur Typdefinitionen (class, struct, delegate) enthalten, alles andere muss in Typen enthalten sein.

Ist das nicht genauso in Java?

18.05.2006 - 16:51 Uhr

Ehrlich gesagt ist das ein bisschen dünne Auskunft.

Wenn man nach Permutation sucht, wird man nicht wenige mathematische Seiten finden, und eher kaum wie man sowas praktisch umsetzt.

Ich würde das ja spontan (bestimmt furchtbar ineffizient) über eine Rekursion lösen.

public void Combine( int length, int ones, string prefix, List<string> results )
{
	if( ones == 0 )
	{
		results.Add( prefix + new string( '0', length ) );
		return;
	}
	
	if( length == ones )
	{
		results.Add( prefix + new string( '1', ones ) );
		return;
	}
	
	Combine( length - 1, ones - 1, prefix + "1", results );
	Combine( length - 1, ones, prefix + "0", results );
}
18.05.2006 - 13:46 Uhr

Es kann aber nicht sein, dass die Klasse zwar gleichen Namen haben, aber in unterschiedlichen Assemblies liegen? Bzw. dass die Assemblies in denen die Typen liegen unterschiedliche Version haben?
Lass dir mal den vollen Namen der Assembly von den Typen anzeigen.

18.05.2006 - 13:32 Uhr

Original von Kostas
Ich habe eine Klasse DAL welche die Datengriffe meistert.
Eigentlich sollte diese nur public static Methoden haben.
Geht jedoch nicht weil ich die connection darin
privat haben möchte.

Und warum kannst du es nicht private static machen?

17.05.2006 - 21:13 Uhr

Original von Golo Haas
Aber zur grundlegenden Vorgehensweise: Das mit den Mausevents bringt Dir nichts, da das Fenster, welches Du per Bildschirmtastatur bedienen willst, sonst den Fokus verliert. Was Du also machen musst, ist, Dich an die Windows-Nachrichten (WM_...) zu hängen, und diese zu verarbeiten und weiterzuleiten.

Auch wir haben sowas geschrieben, und der Weg über Windows-Nachrichten ist nicht nötig.
Die Tastatur kennt das Fenster, das den Fokus hat.
Dann beim Button_Click auf eine Taste wird das Fenster aktiviert (Form.Activate()) und die entsprechende Taste gesendet (SendKeys.Send( taste ))

Allerdings ist das kein eigenständiges Programm sondern direkt Teil der Programme für die das benutzt wird.

14.05.2006 - 22:01 Uhr

Kommt drauf an, was du unter schön verstehst.

Meiner Meinung nach kann es durchaus ein Vorteil sein mit nem beliebigen Datenbank-Explorer nachvollziehen zu können, was das Zeug zu bedeuten hat.

Rein prinzipiell ist die Datenstruktur die du suchst einfach ein Parse-Tree (oder Ableitungsbaum).
Aber da die meisten Datenbanken relational sind, lässt sich sowas (imo) nicht allzu schön darauf übertragen.

Binäre Daten zu speichern ist ja meist auch nicht das Ideale.

14.05.2006 - 13:25 Uhr

Also zur ersten Frage, dazu musst du einfach mal in die Projektoptionen schauen.

Release compiliert ja nicht anders, weil "Release" draufsteht, sondern weil in den Projektoptionen was anderes eingestellt ist.

11.05.2006 - 16:59 Uhr

Bei mir funktionierts auch. Klingt wie das typische Invoke Problem bei mehreren Threads.

11.05.2006 - 15:42 Uhr

Danke, genau sowas habe ich gesucht.

Wer soll denn auch auf die Idee kommen, dass ein CurrencyManager nichts mit Währung zu tun hat.

11.05.2006 - 15:04 Uhr

Eines meiner Formulare hat besitzt eine BindingSource, die als Quelle eine List<> von Objekten benutzt.
Die ListBox benutzt widerum die BindingSource als DataSource, und "Name" als DisplayMember.

Außerdem ist eine TextBox mit der BindingSource und der Property "Name" verbunden.

Das Problem ist:
Wenn in der TextBox der Name editiert wird, wird das erstmal ordentlich auf das von BindingSource ausgewählte Objekt übertragen.
Allerdings wird nicht in der ListBox der neuer Name angezeigt.

Ich hab dafür auch schon eine Lösung:
ich setze DataSource der ListBox erstmal auf null, und setze es dann wieder auf die BindingSource.

Es funktioniert, aber es mutet doch etwas seltsam an. Also wollte ich mal fragen, ob jemand was eleganteres hat.

11.05.2006 - 14:06 Uhr

Ich gehe mal davon aus, du meinst den C# Code.

Durch das using Statement wird automatisch wenn der using Block verlassen wird die Dispose Funktion aufgerufen (für die Variable, die man im Using deklariert). Die ruft dann das Close auf.

11.05.2006 - 13:49 Uhr

Um dir etwas unnötige arbeit zu ersparen:

Wäre es für Testzwecke nicht ausreichend und einfacher, wenn du die Anzeige von einem PropertyGrid übernehmen lässt?

11.05.2006 - 13:44 Uhr

Ich hatte irgendwo mal gelesen, dass es für SharpDevelop ein SVN Plugin geben soll.

Die Suche mit Google war ziemlich sinnlos, da hätte ich genausogut "weiß Schnee" eingeben können.

Auf der offiziellen Website finde ich auch nichts dazu.

Also schau ich mal, ob mir hier geholfen werden kann 🙂

11.05.2006 - 10:55 Uhr

Original von norman_timo
Ich denke das eine ist eine reine Sicht aus der Design/Architektur Ecke, und das andere ist die Umsetzung bei der Implementierung.

Ja darauf können wir uns einigen 😉

11.05.2006 - 10:43 Uhr

Original von norman_timo
Hallo zusammen,

das OO Prinzip gegen das damit verstoßen werden würde ist allgemein unter dem Open/Close Principle bekannt, und besagt:

Offen für Erweiterungen, aber geschlossen für Modifikationen.

Sehr gut erläutert ist das unter folgendem Link (in einem anderen Thread auch schon erwähnt):


>

Gruß
Norman-Timo

Das Open/Close Prinzip hat damit garnichts zu tun.
Das "Close" heißt nur, dass du die Abläufe in einem Modul von außen nicht ändern kannst.
Die Abläufe ändern sich eben nicht, wenn man statt trivialer getter und setter das Feld public macht.

Das steht übrigens bei deinem Link:

They are “Closed for Modification”.
The source code of such a module is inviolate. No one is allowed to make source
code changes to it.

Was auch genau nichts mit Feldern oder Gettern/Settern zu tun hat.

10.05.2006 - 22:41 Uhr

Wartbarkeit und Erweiterbarkeit sind Ziele eines JEDEN Paradigmas. Man denkt sich sowas ja nicht aus, weil draußen gerade schlechtes Wetter ist.

Und das Prinzip was du meinst, lautet viel eher: Ein Objekt kann nicht unerwarteterweise den Zustand eines anderen Objekts ändern. Was bedeutet, das Objekt lässt nur gültige Eingaben zu und wechselt nur in gültige Zustände. Was aber keine triviale Feldzuweisung verbietet, wenn man eben genau das erwartet.

10.05.2006 - 21:29 Uhr

Also ich kenne kein OOP Prinzip gegen das das verstoßen würde.
Irgendwie wird da immer viel reingedeutet, was da garnicht steht.

Es ist wirklich nur ne Frage der Erweiterbarkeit und Wartbarkeit von Code.

Unter den Bedingungen die Kostas gegeben hat, macht es keinen Unterschied für irgendwas. Wenn das mit Properties nicht gegen ein Prinzip verstößt, kann das auch ohne Properties gegen kein Prinzip verstoßen.

Der einzige Grund, warum man es vieleicht nicht so machen sollte ist, dass es ein großer Nachteil bei DLLs ist. Dort ist eine Änderung von Field auf Property eben kein rein semantisches Problem, es muss eben alles neu kompiliert werden. Statt nur ne veränderte DLL auszuliefern, muss man das komplette Programm ausliefern.

10.05.2006 - 19:30 Uhr

Das hat mit OOP genausoviel zu tun wie Properties, nämlich garnichts. Weder im Positiven noch im Negativen, sondern einfach nichts.
Es ist ne Abkürzung für etwas, dass man genausogut mit Funktionen lösen könnte. Nur Zuckergus für einen schönere Syntax und einfachere Implementierung. Einfach das Schlüsselwort event setzen, und du hast das Definieren von 2 Funktionen gespart. Nicht mehr, nicht weniger.

Ich meine du benutzt die Abkürzung "event" und beschwerst dich, dass sie nicht auf dem Hauptweg entlang führt.

10.05.2006 - 19:21 Uhr

Wenn du das nicht willst, lasse wie in meinem zweiten Beispiel einfach das "event" Schlüsselwort bei dem protected Field weg, und setze es eben bei der public Property, und mache adder und remover rein. Wo ist denn das Problem? Du hast schließlich die Entscheidung das Schlüsselwort "event" zu verwenden.

Ich meine das Schlüsselwort event bedeutet:
Du darfst nur adden und removen. Du darfst nicht den Inhalt prüfen. Du darfst nicht den Inhalt setzen. Du darfst nicht ausführen.

Ganz genau betrachtet ist die Ungereimtheit bei der Sache, dass die deklarierende Klasse da die Ausnahme ist. Konsequent ausgeführt, müsste das Schlüsselwort nur auf Properties erlaubt sein. Und dann dürfte auch die deklarierende Klasse diese Property nicht zum ausführen oder setzen benutzen, sondern müsste immer auf das private Delegate-Feld zugreifen. Wenn du dich schon beschwerst, dann bitte darüber.

10.05.2006 - 19:16 Uhr

Original von Lynix
Noch ein Kommentar @Traumzauberbaum

Das hat einfach den Grund, dass man ja nicht immer will, dass eine abgeleitete Klasse das Event auslösen kann. Beispielsweise wenn man von Form ableitet.

Sorry, aber ich bin der Meinung, wenn man nicht will, dass die abgeleitete Klasse all das kann was die Basisklasse kann, dann darf man nicht davon ableiten, bzw. hat einen Fehler im Klassenkonzept. In dem Fall muss man seine Basisklasse wohl eher weiter aufteilen.

Ich würde eher sagen du hast das Prinzip nicht verstanden.

Wenn du von der Klasse ableitest, kannst du alles was die Basisklasse kann. Die Events werden immernoch zu genau den Zeitpunkten ausgeführt, an denen sie in der Basisklasse gedacht sind.
Nur wenn du etwas zusätzliches daran basteln willst, bist du auf das angewiesen, was dir die Basisklasse erlaubt. Und die erlaubt dir eben nicht immer das Ausführen von Events, genausowenig wie sie dir immer erlaubt einen Feldwert zu setzen, eine Funktion auszuführen oder einen Konstruktor zu verwenden.

Ich meine das worüber du dich beschwerst ist ähnlich dazu das "readonly" Schlüsselwort zu verwenden, und sich dann darüber zu beschweren, dass man das Feld nur im Konstruktor setzen kann. Wenn du den Effekt nicht willst, den das "event" Schlüsselwort mit sich bringt, dass du eben nur in der Klasse in der du es deklarierst es auslösen darfst, dann benutze es nicht. Um genau zu sein ist es schon ein Luxus der garnicht nötig gewesen wäre, dass Schlüsselwort "event" nicht nur für Properties zuzulassen. Dann würdest du dich über das "seltsame" Verhalten nicht wundern. Freu dich lieber, dass du nicht für jedes Event wie bei Feldern einen adder und remover schreiben musst, weil dir erlaubt ist es auf Felder anzuwenden.

10.05.2006 - 17:34 Uhr

Man wird dazu nicht gezwungen. Man kann die Events ja genauso behandeln wie Felder:

class Example
{
    protected EventHandler modified;
    public EventHandler Modified
    {
        get {    return modified;    }
        set {    modified = value;    }
    }
}

Wenn du es so machst, ist es ein Feld und verhält sich auch genauso. Du kannst von außen Zuweisen, Abfragen und Aufrufen.

Nur wenn du eben das Schlüsselwort "event" benutzt ist es ein Event und kein Feld. Dann ist es doch völlig legitim, dass es sich nicht mehr genau so wie ein Feld verhalten muss.

10.05.2006 - 17:20 Uhr

Wenn man ein Attribut wirklich mit dem Schlüsselwort "event" dekoriert, kann wirklich nur genau die Klasse es feuern, in der das deklariert ist.

Das hat einfach den Grund, dass man ja nicht immer will, dass eine abgeleitete Klasse das Event auslösen kann. Beispielsweise wenn man von Form ableitet.

Der abgeleiteten Klasse fehlt da auch eigentlich keine Funktionalität. Das Event wird ja immernoch in den Fällen ausgelöst, indenen es bei der Basisklasse eingesetzt wird. Also hats genau die gleiche Funktionalität, nur eben auch nicht mehr.

Um eben das abfeuern auch in abgeleiteten Klassen zu erlauben, benutzt man üblicherweise eine (meist virtuelle) Funktion OnXXX. Die stellt die Basisklasse zur Verfügung und löst damit das Event aus.

Beispiel:


public class BaseClass
{
    private string value;
    public string Value
    {
        get {    return value;    }
        set
        {
            this.value = value;
            OnModified( EventArgs.Empty );
        }
    }

    public event EventHandler Modified;
    protected virtual void OnModified( EventArgs e )
    {
        if( Modified != null )
            Modified( this, e );
    }
}

public class Derived : BaseClass
{
    private string key;
    public string Key
    {
        get {    return key;    }
        set
        {
            this.key = value;
            OnModified( EventArgs.Empty );
        }
    }
}
08.05.2006 - 20:59 Uhr
  1. Falsches Ergebniss

Die While Schleifendurchläufe zählen eben nicht wieviele Versuche gebraucht wurden.

Als Beispiel fängst du mit (0,0) an, Anzahl = 0.
Dann gehst du alles durch und hast 2 neue Knoten in der Schlange: (3,0) und (0,4) Anzahl = 1
Dann gehst du für (3,0) alle Möglichkeiten durch -> Anzahl = 2
Dann gehst du für (0,4) alle Möglichkeiten durch -> Anzahl = 3

Obwohl du für (0,4) nur eine Aktion gebraucht hast, ist an dieser Stelle Anzahl schon 2.

Du musst einfach für jeden Knoten die Tiefe speichern. (0,0) hat Tiefe 0.
Und Induktiv hat jeder Knoten eine Tiefe mehr als der Knoten, aus dem er hervorgeht (current)

  1. Weg zurückverfolgen

Da gibt es zwei schöne Möglichkeiten

Entweder du nummerierst die Aktionen durch, und merkst dir statt dem bool in dem Array die Zahl der Aktion mit der du dorthin gekommen bist. 0 wäre dann "noch nicht gefunden", 1 wäre Aktion 1 usw. Dann kannst du rekonstruieren von welchem Knoten du gekommen bist, und kannst so bis zur Wurzel (0,0) den Weg zurück finden.

Andersrum könntest du dir merken von welchem Knoten aus du beim aktuellen gelandet bist, und daraus dann die Aktion bestimmen.

08.05.2006 - 14:23 Uhr

Versuch mal als letzten Parameter einen StringBuilder (ohne ref)

08.05.2006 - 13:00 Uhr

_Original von Lexodus_Nachdem was ich über .NET Remoting gelesen habe, werden dort ganze Objekte mit Marshalling übertragen. Was bedeutet, dass auf dem Client und dem Server die gleichen Typen von Objekten verfügbar sein müssen, ist das richtig?

Das ist unterschiedlich. Es gibt ja die MarshalByValue und MarshalByReference.

Bei MarshalByValue wird das komplette Objekt übertragen, und rekonstruiert. Ich würde meinen, dafür müssen die Typen schon voll übereinstimmen, habs aber nicht getestet.

Bei MarshalByReference braucht man eigentlich nichts. Es ist aber empfehlenswert zumindest ein gemeinsames Interface zu benutzen, da es ohne Funktionen doch recht sinnlos ist 😉 Da werden nur die Funktionsaufrufe übertragen, und die Funktion selbst eben nur auf dem echten Objekt ausgeführt. Deswegen muss die eine Seite die Implementation nicht unbedingt kennen.

07.05.2006 - 10:17 Uhr

Sonst hast du nichts zur Verteidigung deiner Abhandlung zu sagen?

Du formulierst das alles in der Art: "Wann kann ich Labels Methoden vorziehen"
Inwiefern ist das keine Richtlinie?

Wirklich peinlich wird das ganze, weil du einerseits auf deiner Meinung beharrst und dich aber nicht zu den Problemen die hier vorgetragen wurden äußerst.

07.05.2006 - 09:29 Uhr

Also ich kenne nur die.

Man sollte dabei aber aufpassen, wenn man die synchrone Variante benutzt. (also Send, nicht BeginSend)
Standardmäßig ist eine Option eingestellt, die erstmal zu sendende Daten in größeren Packeten sammelt (Nagle Algorithmus). Dadurch wird der Send erst kurze Zeit später ausgeführt, und man befindet sich höchstwahrscheinlich nicht mehr im try Block.

Die Option heißt NoDelay und die sollte man dafür anschalten.

06.05.2006 - 22:23 Uhr

Ich wollte auch ehrlich keinen beleidigen.
Man muss nicht alles können, ich bin kein Freund von Low-Level Problemen, ich entwerfe lieber Algorithmen als Operationen zu zählen.
Und wenn der Threadersteller damit klarkommt und sich das für ihn bewährt hat, soll er doch weiter so Programmieren.
Das Einzige was mir an der Sache eben nicht gefällt ist, dass es eben so als Richtlinie formuliert wurde. Man sollte soetwas nur formulieren, wenn man sich auf dem Gebiet auch sicher bewegen kann.

Die grundlegende Richtline in allen höheren Programmiersprachen ist eben, dass gotos zu meiden sind. Denn sie erschweren im Allgemeinen das Debuggen, die Lesbarkeit, die Erweiterbarkeit und die Wiederverwendbarkeit von Code.
Und man sollte diese Richtlinie erst verlassen, wenn man weiß worauf man sich einlässt. Sprich es muss dazu keine Anleitungen für Anfänger geben, weil Anfänger das nicht machen sollten. (Als Assembler Programmierer weiß man ja ziemlich gut wie man mit gotos umgehen muss)

Ich bin auch schon oft leichtfertig an kleine Programme rangegangen mit dem Gedanken im Hinterkopf, dass ich den Code nie wieder sehen muss. Und dann ist das Programm eben nur ne Sammlung von 2-6 größeren Funktionen, die alle nicht sonderlich durchschaubar sind. Und viel zu schnell kommt sowas wie ein Bummerang zurück und man wirft für eine kleine Änderung den halben Code weg.

06.05.2006 - 17:49 Uhr

Die genannten Gründe lassen sich eigentlich auf einen Nenner bringen. Dir fehlt die Erfahrung mit objektorientierter oder auch nur strukturierter Programmierung. Daraus Empfehlungen zur Nutzung einer objektorientierten Sprache abzuleiten halte ich für sehr gewagt.

Viele der Probleme die du auflistest empfinde ich als völlig konstruiert.
Was muss man sich für Gedanken wegen ref, out und params? Entweder man braucht es oder nicht.
Was ist so toll daran, dass die Variablen erhalten bleiben? Wenn ich ne Variable in einer Methode brauche, wird sie übergeben und fertig. Ich sehe das Problem absolut nicht.

Und mal ganz ehrlich. Wenn eine Subroutine nur von einer Stelle aufgerufen wird, gibt es nur zwei Gründe das nicht direkt da rein zu schreiben: Wartbarkeit und Wiederverwendbarkeit. Ein goto trägt dazu nichts bei.

06.05.2006 - 00:51 Uhr

Das liegt an den beiden Funktionen.
size ist vom Datentyp int.
Damit wird das Ergebniss size * 1024 ebenfalls zum int
Und damit wird dann die division als Integerdivision ausgeführt. Die Nachkommastellen werden einfach weggelassen.

Beheben kannst du das, wenn du den linken Operanden zum float oder double castest. Es wird sich immer nach dem linken Operanden gerichtet und die entsprechende Operation ausgefürt. Wenn also der linke Operand float ist, wird auch das Ergebniss ein float.

return ( ((float)size * 1024 / rate) / 60);

05.05.2006 - 15:54 Uhr

Durch Close wird normalerweise auch ein Dispose ausgeführt.

Zumindest hab ich es so in Erinnerung. Wenn man Close auf ein Formular aufruft, kann man es nicht wieder per Show zeigen. Man muss ein neues erstellen.

Das ist imo nur dann nicht der Fall, wenn du das Closing Event entsprechend behandelst.

05.05.2006 - 15:52 Uhr

Gibt es eigentlich einen Grund, warum du das WriteXmlAsStream nicht auf nen FileStream anwenden kannst?

Ansonsten hilft nunmal alles nichts, du musst die Read und Write Operationen auf den Streams benutzen.

04.05.2006 - 15:58 Uhr

Oh na so kann man es natürlich auch sehen.
Die 9 hatte ich angenommen, weil ich dachte es sei nicht wichtig wieviel in dem anderen ist, hauptsache einer hat 2 Liter drin. also (2,0) (2,1) ... (2,4) und (0,2) ... (3,2)

Und mit dem Stack hab ich mich übrigens geirrt, es muss ein Queue sein, also First-In-First-Out. Mit Stack wärs Tiefensuche, du brauchst Breitensuche.
Die Queue soll nur dazu da sein dir zu merken, mit welchen Zuständen du weiter probierst. Weil eben das was du zuerst findest auch zuerst wieder verarbeitet wird, gehst du so erstmal alle Zustände durch, zu denen du mit 1 Schritt gekommen bist, erst dann alle mit 2 Schritten, usw.


bool[] besucht = new bool[4,5];
Queue<CKnoten> queue = new Queue<CKnoten>();
stack.Push( new CKnoten( 0, 0 ) );
besucht[0,0] = true;

CKnoten current, next;
while( queue.Count != 0 ) // Queuenicht leer
{
    current = queue.Dequeue();
    if( irgendwie_auf_Endzustand_testen() ) break;

    next = new CKnoten( current.Kanister1, current.Kanister2 );
    next.FuellKanister1();
    if( !besucht[next.Kanister1,next.Kanister2] )
    {
        besucht[next.Kanister1,next.Kanister2] = true;
        queue.Enqueue( next );
    }
    next = new CKnoten( current.Kanister1, current.Kanister2 );
    next.FuellKanister2();
...
}

irgendeine_ausgabe();

wohlgemerkt für .NET 2.0
Und bool für besucht wird dir auch nicht ausreichen, weil du ja irgendwie den Weg rekonstruieren willst. Müsstest dir eben merken, mit welchem Schritt du dahin gekommen bist.

wäre vieleicht clever wenn du Fuellen usw. so implementierst, dass sie gleich selber ne neue, bearbeitete CKnoten-Instanz zurückgeben.

EDIT: bin schwer Stackgeschädigt im Moment 😉

04.05.2006 - 15:01 Uhr

Ich hätte dazu noch nen Optimierungsvorschlag.
Die Kanister haben ja einen Füllzustand. Die kleinste Einheit die man herstellen kann ist ja 1 Liter.
Die Zustände von Kanister a sind also {0,1,2,3} und von Kanister b {0,1,2,3,4}
Also hat man insgesamt nur {0,1,2,3}x{0,1,2,3,4} Zustände, macht 4*5 = 20.

Davon sind 5 + 4 = 9 Endzustände, weil dann in einem der beiden Kanister 2 Liter drin sind.

Du hast also nen Graphen mit 20 Knoten. Davon ist einer als Startknoten ausgezeichnet (0,0) und 9 als Zielknoten.

Und es ist klar: wenn du am selben Zustand bist, hast du die gleichen Wege wie du fortsetzen kannst.

Du merkst dir also wo du schon warst in dem Array.
Du holst einen Zustand vom Stack, und probierst alle Aktionen.
Du steckst neu gefundene Zustände auf den Stack.
Wenn ein Endzustand erreicht ist, hast du ihn aufgrund des Stacks (Breitensuche) auch sicher mit der geringsten Anzahl an Schritten erreicht. Hast dir hoffentlich gemerkt wie du dahin gekommen bist 😉

03.05.2006 - 00:00 Uhr

Gegeben:
string mit einem Hexadezimalwert (z.B. "0x1230ad0b0a23567dfb")

Gesucht:
byte[] mit entsprechenden Werten (z.B. { 12, 30, ad, 0b, 0a, 23, 56, 7d, fb })

Die Suche hat nichts schönes ausgespuckt. Mit einer Schleife je 2 Werte umwandeln ist kein Thema. Mir ist nur aufgefallen, dass es bei XmlSerialization die Möglichkeit gibt ein byte[] Feld eben so in dem serialisierten Xml darzustellen.

Ich frage mich also, ob es dazu eine schöne Funktion gibt.

Außerdem bin ich noch unentschlossen, ob ich das in Little Endian oder Big Endian speichern soll. Wie ist denn der Standard in .NET dabei?

02.05.2006 - 22:41 Uhr

zum Teilen gibts ne Funktion:

string line = reader.ReadLine();
string[] words = line.Split( ' ' );

finde das nicht wirklich umständlich

01.05.2006 - 18:25 Uhr

Deine Annahme muss so nicht unbedingt stimmen.
Deine Rechnung ist zwar ok, aber man kommt ja andersrum genauso aufs Ergebniss.

a gibt ja den erhöhten Wert zurück
a
gibt zuerst den Wert zurück und erhöht erst danach

Bei Auswertung von Rechts nach Links würde zuerst 1 geschrieben, a auf 2 erhöht, dann a auf 3 erhöht, und 3 geschrieben. (3 + 1) Kommt ebenfalls 4 raus.

Aber ich würde mal behaupten sowas zählt zu undefinierten Verhalten. Sprich es wird im C# Standard nicht festgelegt sein, wie es zu verarbeiten ist. Im jeweiligen Compiler ist es natürlich deterministisch, er wird immer das gleiche machen. Aber bei nem anderen Compiler, oder auch nur ner anderen Version, kann das Verhalten anders sein.

In diesem Beispiel ist es ja egal wie rum, aber wenn man zweimal a++ als Index in einer Zeile verwendet, ist es eben nicht mehr egal.
Oder eben bei sowas schon: a * a

01.05.2006 - 17:54 Uhr

Das zweite muss nicht unbedingt ne implizite Typumwandlung sein. Es reicht ja wenn der Operator mit int definiert ist. Und das ist wohl der Fall.

30.04.2006 - 14:02 Uhr

Du hast aber kein VB Projekt erstellt oder?

29.04.2006 - 14:28 Uhr

Ich glaube er meint einfach nur, dass du nicht jede Ziffer einzeln betrachtest, sondern in Blöcken. Die Blöcke wandelst du dann in ints um und addierst sie, statt jede einzelne Ziffer in einen int umzuwandeln. Bei Dezimalsystem sollten Blöcke von 9 Ziffern problemlos ohne Überlauf addiert werden können.

Also zuerst die Umwandlung in int Arrays indem du in Blöcke teilst.
Dann die Rechnung.
Und dann zur Ausgabe wieder zurückwandeln.

29.04.2006 - 11:02 Uhr

Googel einfach mal nach Karatsuba Ofman. Gibt bestimmt millionen Leute die das besser erklären können als ich.

28.04.2006 - 23:01 Uhr

Fürs Multiplizieren kenne ich noch ne einfache Performanceverbesserung.

Und zwar den Karatsuba-Ofman-Algorithmus. Im Prinzip eine Devide&Conquer Strategie.

Du teilst den größeren der beiden Strings in der Mitte und zerlegst ihn quasi in:
x = x0 + x1*(b^n/2)

Wobei x0 und x1 jeweils halbe Länge von x haben.
Also wenn x die Länge n hat, sind x0 die niederwertigen n/2 Stellen, x1 die höherwertigen n/2 Stellen. b ist die Basis, also im Dezimalsystem 10.

Den 2. Operator (y) musst du in gleicher Weise teilen. Also nicht in der Mitte, sondern (von rechts nach links) in n/2 niederwertige Stellen und Rest.

Der Rest ist dann simple Mathematik:

x * y = ( x0 + x1*(bn/2) ) * ( y0 + y1*(bn/2) )
x * y = x0y0 + x1y1b^n + (x1y0 + x0*y1)*b^n/2

(Bisher 4 Multiplikationen mit Länge n/2 statt 1 Multiplikation mit Länge n. Noch nichts gespart)

NR:
(x0 + x1) * (y0 + y1) = x0y0 + x1y1 + (x0y1 + x1y0)
(x0y1 + x1y0) = (x0 + x1) * (y0 + y1) - x0y0 - x1y1

x0y0 und x1y1 sind bereits bekannt. Also können wir (x0y1 + x1y0) mit nur einer Multiplikation der Länge n/2 und zwei Subtraktionen ausführen.

Also in Code würde das so aussehen:

public static string Multiply( string x, string y )
		{
			// unnötige Länge kürzen
			x = x.TrimStart( '0' );
			y = y.TrimStart( '0' );
			
			if( x.Length < y.Length )
			{
				string buff = x;
				x = y;
				y = buff;
			}
			
			if( string.IsNullOrEmpty( y ) )
				return "0";
			
			if( x.Length > 4 )	// Abbruchbedingung für die Rekursion
			{
				// x und y teilen
				string x0 = x.Substring( x.Length / 2 );
				string x1 = x.Substring( 0, x.Length - x0.Length );
				
				string y0, y1;
				if( x0.Length > y.Length )
				{
					y0 = y;
					y1 = "0";
				}
				else
				{
					y1 = y.Substring( 0, y.Length - x0.Length );
					y0 = y.Substring( y1.Length );
				}
				
				string a = Multiply( x0, y0 );
				string b = Multiply( x1, y1 );
				string c = SumUp( a, b );
				string d = Substract( Multiply( SumUp( x0, x1 ), SumUp( y0, y1 ) ), c );
				
				return SumUp( SumUp( a, b + new string( '0', x0.Length*2 ) ), d  + new string('0', x0.Length) );
			}
			else	// länge ist klein genug für Integermultiplikation ohne Überlauf
			{
				int ix = int.Parse( x );
				int iy = int.Parse( y );
				return (ix * iy).ToString();
			}
		}

Hab das mal ganz primitiv gegen deine Implementation antreten lassen:

		static void Main()
		{
			string a = "1234567890123456789012345678901234567890";
			string b = "9876543210987654321098765432109876543210";
			
			int max = 5000;
			
			int start = Environment.TickCount;
			for( int i = 0; i < max; ++i )
			{
				Multiply( a, b );	// Karatsuba-Ofman
			}
			Console.WriteLine( Environment.TickCount - start );
			
			start = Environment.TickCount;
			for( int i = 0; i < max; ++i )
			{
				Multiply2( a, b );	// deins
			}
			Console.WriteLine( Environment.TickCount - start );
			
			Console.ReadKey();
		}

Output (ca):
21000
35000
(lange zahl)
(noch eine lange zahl die gleich der 1. ist)

Also schon messbar schneller.
Wenn große Abschnitte nur 0 sind, wirds sogar noch deutlich besser.

Das ist so ein Trick, den man im Informatikstudium 3. Semester lernt, und auch wie man sieht nach dem Verständniss auch leicht zu implementieren ist 😁
Man könnte vieleicht noch schauen, ob man an der Abbruchlänge von 4 noch was optimieren könnte. Habs eben nur gewählt wegen der int Multiplikation. Keine Ahnung ob ein früherer Abbruch mit deiner Multiplikation am Ende nicht schneller wäre.

Ich hoffe das alles klingt nicht zu klugscheißerisch 😁

28.04.2006 - 21:41 Uhr

Warum hast du denn das ISet<T> Interface explizit implementiert?
Ich seh da keinen Unterschied zu den anderen Funktionsdefinitionen.

Außerdem gefällt mir die * Operation nicht.
Eigentlich wärs schöner, wenn du &, | und \ überschreibst für Schnitt (was in beiden ist) Vereinigung (was in mindestens einem ist) und.
Einfach weil die Mengenoperationen Schnitt und Vereinigung sehr ähnlich zu den Bit-Operationen UND und ODER sind. Auch vom Symbol her.
Und A\B ist in der Mathematik der offizielle Operator für "A ohne B", was bei dir "-" ist. "+" und "-" könntest du ja beibehalten um einzelne Elemente hinzuzufügen, das gibts ja in Mathe nicht, da muss man ja erst ne einelementige Menge erzeugen.

Eine Art Multiplikation ist ja für Mengen auch definiert:

A*B = { (x,y) : x element A, y element B }

Dazu bräuchtest du noch eine Tupel-Implementation. (Bzw. Vektor)
Tupel ist aber nicht mehr Generisch möglich, weil im Prinzip jeder Typ mit allen möglichen anderen Typen in unbegrenzter Art verknüpft sein kann. Also z.B. auch (int, string, int, decimal, string, ...). Außer du definierst das Rekursiv... LinkedList artig... hmm 😉 also Tupel<int,Tupel<string,Tupel<int,Tupe<decimal,string>>>>

also:

foreach( T x in A )
{
    foreach( T y in B )
    {
        result.Insert( new Tupel( x, y ) );
    }
}

Wobei du bei Mengen und Tupeln aufpassen müsstest, dass alle gleiche Dimension haben.

Ist aber alles eigentlich nicht so wichtig 😁 Nur ein paar Vorschläge.

Iterieren kann man übrigens in Mathe wie auch in Pascal (oder Delphi). In Mathe sogar auf abzählbar unendlichen Mengen, glaub das geht in C# nicht 😜

28.04.2006 - 14:07 Uhr

Du könntest eine Klasse anlegen, die beide Objekte enthält.

Damit du die dann editieren kannst, brauchst du einen TypeConverter für die Klassen, der von ExpandableObjectConverter erbt. Ist nicht ganz kleines Thema, aber am Ende musst du eigentlich nur ein paar Methoden überschreiben:

public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)		
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)

Das sieht dann so aus, wie z.B. die Size Property bei nem beliebigen Control, die du auffallten kannst zu Width and Height.
Einziger Nachteil: die Kategorien der Klassen gehen verloren.

27.04.2006 - 23:36 Uhr

Also die aller einfachste (und imo auch praktikabelste) Lösung ist, dass du einfach mit deinem Form Editor noch einen RadioButton erstellst und genau auf den Multiplizieren RadioButton legst, und Visible auf "false" setzt.

Dann beim Knopfdruck machst du nur noch:
radAddition.Visible = false;
radWurzel.Visible = true;

Das Prinzip benutze ich zumindest ständig in meinen Formularen.
Ist deutlich weniger mühselig als Controls zu erstellen, zu Platzieren und zu konfigurieren.

Aber um auch die Fehler an deiner Lösung zu erklären:

  • Der RadioButton ist jetzt quasi im luftleeren Raum. Du musst ihm erst dem Formular zuordnen. Das sollte z.B. mit "this.Controls.Add( radWurzel );" funktionieren
  • zusätzlich müsstest du den radAddition noch entfernen über "this.Controls.Remove( radAddition );"
  • Variablennamen kann man nicht ändern. Durch die Zuweisung "radAddition = radWurzel" erklärst du nur, dass radAddition den Wert von radWurzel annimmt.