Laden...

Das Programmier-Spiel: nette Übungsaufgaben für zwischendurch

Erstellt von abra_joe vor 14 Jahren Letzter Beitrag vor 3 Jahren 771.547 Views
C
401 Beiträge seit 2007
vor 13 Jahren

zu 1: Wie du du die Performance erlangst ist deine Sache. Wenn du eine vernünftige Lösung mit Threads erstellt ist das natürlich super.

zu 2: Ja, das soll die Potenz sein

zu 3: pure bedeutet, dass die Methode keine Seiteneffekte hat, somit das eigentliche Objekt nicht verändert. Außerdem darf die Methode nicht auf irgendwelchen Werten basieren, die sich zur Laufzeit de Programms verändern können. Nur so kann sichergestellt werden, dass z.B. bei


int[] args = {1,2,3,4};
Matrix m = new Matrix(2, args);

m*5
m*5
m*5
m*5

immer das gleiche Ergebnis herauskommt. Siehe auch http://en.wikipedia.org/wiki/Pure_function

zu 4: ja, so zum Beispiel

Achso... der Einfachheit halber: Die Matrix ist quadratisch und fehlende Werte sind mit 0 aufzufüllen. z.B. sähe dann eine Matrix der größe 2, die nur den Wert 1 bekommt so aus:

1 0
0 0

Wie in meinem Beispiel schon zu sehen ist der erste Parameter die Größe der Matrix, der zweite ein Array mit den Werten.

1.130 Beiträge seit 2007
vor 13 Jahren

Wie in meinem Beispiel schon zu sehen ist der erste Parameter die Größe der Matrix, der zweite ein Array mit den Werten.

warum keine mehrdimensionalen arrays?

Der code ist ziemlich lang, unter anderem, weil man für negateive exponenten eine invertierte matrix braucht.
Threadsafe ist es, da immuteable. Die Potenzierung ist etwas optimiert, aber weiter opßtimierungen würden den code noch deutlich verlängern.


    public class MaTrix
    {
        public MaTrix(float[,] array)
        {
            this.array=(float[,])array.Clone();
            width=array.GetUpperBound(0)+1;
            height=array.GetUpperBound(1)+1;
        }

        public MaTrix Clone()
        {
            return new MaTrix((float[,])array.Clone());
        }

        int width;
        public int Width
        {
            get { return width; }
        }

        int height;
        public int Height
        {
            get { return height; }
        }

        private float[,] array;
        public float this[int x, int y]
        {
            get
            {
                return array[x, y];
            }
        }

        public static MaTrix CreateIdentity(int num)
        {
            MaTrix C=new MaTrix(new float[num,num]);
            for(int i=0;i<num;i++)
                C.array[i,i]=1;
            return C;
        }

        public static MaTrix operator +(MaTrix A, MaTrix B)
        {
            if (A.width != B.width || A.height != B.height)
                throw new Exception("Dimensions not Equal");
            MaTrix C = new MaTrix(new float[A.width, B.height]);
            for (int y = 0; y < C.height; y++)
                for (int x = 0; x < C.width; x++)
                {
                    C.array[x, y] = A.array[x, y] + B.array[x, y];
                }
            return C;
        }

        public static MaTrix operator -(MaTrix A, MaTrix B)
        {
            if (A.width != B.width || A.height != B.height)
                throw new Exception("Dimensions not Equal");
            MaTrix C = new MaTrix(new float[A.width, B.height]);
            for (int y = 0; y < C.height; y++)
                for (int x = 0; x < C.width; x++)
                {
                    C.array[x, y] = A.array[x, y] - B.array[x, y];
                }
            return C;
        }


        public static MaTrix operator *(MaTrix A, MaTrix B)
        {
            if (A.width != B.height)
                throw new Exception("Dimensions");
            MaTrix C = new MaTrix(new float[B.width, A.height]);
            for (int y = 0; y < C.height; y++)
                for (int x = 0; x < C.width; x++)
                {
                    float temp=0;
                    for (int i = 0; i < C.height; i++)
                    {
                        temp += A.array[i, y] * B.array[x, i];
                    }
                    C.array[x, y]=temp;
                }
            return C;
        }


        public static MaTrix operator *(float scalar, MaTrix B)
        {
            MaTrix C = new MaTrix(new float[B.width, B.height]);
            for (int y = 0; y < C.height; y++)
                for (int x = 0; x < C.width; x++)
                {
                    C.array[x, y] = scalar * B.array[x, y];
                }
            return C;
        }

        public static MaTrix operator ^(MaTrix B, int exp)
        {
            if(B.width!=B.height)
                throw new Exception("Matrix has to be quadratic");
            if(exp==0)return CreateIdentity(B.width);
            if(exp<0)
            {
                B=MaTrix.Invert(B);
                exp=-exp;
            }
            int pCount=0;
            for(int p=exp;p!=0;p/=2)
                pCount++;
            MaTrix[] pows=new MaTrix[pCount];
            pows[0]=B;//pCount ist nur 0, wenn exp das auch ist, was schon abgefragt wurde
            for(int i=1;i<pCount;i++)
            {
                pows[i]=pows[i-1]*pows[i-1];
            }
            MaTrix C=null;
            bool first = true;
            for(int p=exp,i=0;p!=0;p/=2,i++)
            {
                if ((p & 1) != 0)
                {
                    if (first)
                    {
                        C = pows[i];
                        first = false;
                    }
                    else
                        C *= pows[i];
                }
            }
            return C;
        }

        public static MaTrix Invert(MaTrix A)
        {
            if(A.width!=A.height)
                throw new Exception("Matrix has to be quadratic");
            return new MaTrix(MatrixInversion(A.array, A.width));
        }

        public float[,] ToArray()
        {
            return (float[,])array.Clone();
        }

        static float[,] MatrixInversion(float[,] A, int size)
        {
            double det = 1.0 / CalcDeterminant(A, size);
            float[,] minor = new float[size - 1, size - 1];
            float[,] C=new float[size,size];
            for (int j = 0; j < size; j++)
            {
                for (int i = 0; i < size; i++)
                {
                    GetMinor(A, minor, j, i, size);
                    C[i, j] = (float)(det * CalcDeterminant(minor, size - 1));
                    if ((i + j) % 2 == 1)
                        C[i, j] = -C[i, j];
                }
            }
            return C;
        }

        static int GetMinor(float[,] src, float[,] dest, int row, int col, int size)
        {
            int colCount = 0, rowCount = 0;
            for (int i = 0; i < size; i++)
            {
                if (i != row)
                {
                    colCount = 0;
                    for (int j = 0; j < size; j++)
                    {
                        if (j != col)
                        {
                            dest[rowCount,colCount] = src[i,j];
                            colCount++;
                        }
                    }
                    rowCount++;
                }
            }
            return 1;
        }

        static double CalcDeterminant(float[,] mat, int size)
        {
            if (size == 1)
                return mat[0, 0];
            double det = 0;
            float[,] minor = new float[size - 1, size - 1];
            for (int i = 0; i < size; i++)
            {
                GetMinor(mat, minor, 0, i, size);
                det += /*pow( -1.0, i )*/((i & 1) == 0 ? 1 : -1) * mat[0, i] * CalcDeterminant(minor, size - 1);
            }
            return det;
        }


        public override string ToString()
        {
            List<string> list = new List<string>(2*width+height);
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    list.Add(array[x, y].ToString("#0.0000").PadRight(10));
                    list.Add("  ");
                }
                list.Add(Environment.NewLine);
            }
            return string.Concat(list.ToArray());
        }
    }

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!

C
401 Beiträge seit 2007
vor 13 Jahren

Hi floste,

es sind zwar noch ein paar kleine Schönheitsfehler drin (2.0F * matrix möglich matrix * 2.0F nicht) und wenn eine Matrix keine Inverse besitzt liefert sie eine Matrix mit NaN gefüllt zurück, aber die Inverse war eh nicht gefordert und im groben läuft es ja. Also sehe ich die Aufgabe als erfüllt an.

Dann hau ma die nächste raus.

1.130 Beiträge seit 2007
vor 13 Jahren

Mal was kurtzes:

Die tcp/ip checksumme über ein array berechnen. Kümmert euch dabei weder um pseudoherder, headerstruktur oder sonstwas tcp-spezifisches, noch um little oder big-endian ,denn es kommt unabhängig vom der endianness sowieso die gleiche bytefolge raus.

Die funktion soll also folgende Signatur haben:

public ushort Checksum(byte[] data)//data kann auch eine ungrade länge haben!
{
...

Beschreibung des Algorithmus:
http://wwwse.inf.tu-dresden.de/data/courses/SE1/exercises/se_ws0405_exercise8_tcp_checksum.pdf

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!

479 Beiträge seit 2008
vor 13 Jahren

public ushort Checksum(byte[] data)//data kann auch eine ungrade länge haben!
{
    int checksum=0;
    for (int i=0; i < data.Length; i++)
    {
        if((i & 1) != 0)
            checksum += data[i] * 256;
        else 
            checksum += data[i];
    }

    while ((checksum & (~(int)0xFFFF)) != 0)
        checksum =(checksum & 0xFFFF) + (checksum >> 16);

    return (ushort)((~checksum)&0xFFFF);
}

müsste klappen...

mfg.
markus111

[Follow me on Twitter](http://twitter.com/blendingsky)
1.130 Beiträge seit 2007
vor 13 Jahren

Ein kleiner Schreibfehler ist drin:checksum und nicht sum.

Aber also irgendwie habe ich da so eine Ahnung, dass der Code nicht wirklich von dir stammt hust.

Wie dem auch sei: Der Code ist korrekt (vom Schreibfehler abgesehen) und erfüllt die Aufgabe und es wäre witzlos, wenn eine korrekt6e und vollständige Lösung hier steht und ein anderer eine weitere schreiben müsste: stell die nächste aufgabe....

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!

479 Beiträge seit 2008
vor 13 Jahren

Ich weiß dass du mir den Code dafür irgendwann mal gegeben hast, aber ich hatte keine Lust ihn rauszusuchen. Der Schreibfehler kommt daher, dass ich den Code nicth getestet hab 😁

Gut, nächste Aufgabe (bestimmt zu einfach):
Auf der Konsole soll eine Sinus-Welle ausgegeben werden:

                                                      ############
                                                    ###          ###
                                                   ##              ##
                                                 ##                  ##
                                                ##                    ##
                                               ##                      ##
                                             ##                          ##
                                            ##                            ##
                                           ##                              ##
                                          ##                                ##
                                         ##                                  ##
                                        ##                                    ##
#                                      ##                                      #
##                                    ##
 ##                                  ##
  ##                                ##
   ##                              ##
    ##                            ##
     ##                          ##
       ##                      ##
        ##                    ##
         ##                  ##
           ##              ##
            ###          ###
              ###########

So in etwa sollte das Ergebniss aussehen.

mfg.
markus111

[Follow me on Twitter](http://twitter.com/blendingsky)
1.002 Beiträge seit 2007
vor 13 Jahren

Hallo markus111,

hier meine Lösung. Dauert zwar ein paar Sekunden, aber Effizienz war ja auch nicht Teil der Anforderung.


using System;

namespace SinusConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            int xWidth = 63;
            int yHeight = 10;

            for (double i = 0; i < xWidth; i += 0.0005)
            {
                double y = (Math.Sin(i / xWidth * 2 * Math.PI) + 1) * yHeight;
                
                Console.SetCursorPosition(
                    (int)Math.Round(i),
                    (int)Math.Round(y)
                );
                Console.Write('#');
            }

            Console.Read();            
        }
    }
}

m0rius

Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg

479 Beiträge seit 2008
vor 13 Jahren

Jup, richtig. Ich hattes es so, dass es über die komplette Breite geht, aber das ist schon okay. Du bist dran 🙂

EDIT: Zu deiner Effizenz: den i Wert nur um das wirklich benötigte erhöhen, also 1 / xWidth.

mfg.
markus111

[Follow me on Twitter](http://twitter.com/blendingsky)
1.002 Beiträge seit 2007
vor 13 Jahren

Hallo markus111,

kannst das ja gerne mal ausprobieren ... Es kommt nicht die gleich Ausgabe bei raus 😉.

m0rius

Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg

1.002 Beiträge seit 2007
vor 13 Jahren

Hallo zusammen,

nächste Aufgabe:

Es soll eine Methode geschrieben werden, die alle glücklichen Zahlen bis zu einer festlegbaren Obergrenze [tt]upperLimit[/tt] zurückgibt. Es soll dabei die folgende Signatur verwendet werden:
[CSHARP]public int[] GetLuckyNumbers(int upperLimit)[/CSHARP]Alles Weitere findet ihr im entsprechenden [URL]Wikipedia-Artikel[/URL].

Ich wünsche euch viel Spaß!

m0rius

Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg

K
593 Beiträge seit 2007
vor 13 Jahren

Hallo m0rius,

ich hab da mal was gebastelt was wirklich ziemlich hässlich ist aber es funktioniert ^^; Ich glaube ich habe da ziemlich quer gedacht.. das ganze kann sicherlich deutlich einfacher gemacht werden aber ich habe heute nen Wurm im Kopf. Naja meine Lösung:


        static void Main(string[] args)
        {
            int upperLimit = Convert.ToInt32(Console.ReadLine());
            int[] LuckyNumbers = GetLuckyNumbers(upperLimit);
            for (int i = 1; i <= LuckyNumbers.Length; i++)
            {
                Console.WriteLine(LuckyNumbers[i-1].ToString());
            }
            Console.ReadLine();
        }

        public static int[] GetLuckyNumbers(int upperLimit)
        {
            int[] LuckyNumbers = new int[upperLimit];
            int erase = 2;
            int eraseCounter = 1;
            for (int i = 1; i <= upperLimit; i++)
            {
                LuckyNumbers[i - 1] = i;
            }
            while (erase < LuckyNumbers.Length)
            {
                int[] newLuckyNumbers = new int[LuckyNumbers.Length];
                int digit = 1;
                for (int i = 0; i < LuckyNumbers.Length; i++)
                {
                    if (digit == erase)
                    {
                        digit = 0;
                    }
                    else
                    {
                        newLuckyNumbers[i] = LuckyNumbers[i];
                    }
                    digit++;
                }
                LuckyNumbers = new int[LuckyNumbers.Length - (LuckyNumbers.Length / erase)];
                int x = 0;
                foreach (int number in newLuckyNumbers)
                {
                    if (number > 0)
                    {
                        LuckyNumbers[x] = number;
                        x++;
                    }
                }
                erase = LuckyNumbers[eraseCounter];
                eraseCounter++;
            }
            return LuckyNumbers;
        }

795 Beiträge seit 2006
vor 13 Jahren

AHHHH!

Vier Minuten zu spät.....

Ich poste mal trotzdem meine Lösung, aber Kaji war leider schneller... 😜 :evil:

public static Int32[] GetLuckyNumbers(Int32 upperLimit)
{
    var luckyNumbers = new List<Int32>(new Int32[upperLimit]);

    for (var i = 1; i <= upperLimit; i++)
    {
        luckyNumbers[i - 1] = i;
    }

    var step = 2;
    var stepIndex = 1;

    while (luckyNumbers.Count >= step)
    {
        var numsToRemove = new List<Int32>();

        for (var i = step - 1; i < luckyNumbers.Count; i += step)
        {
            numsToRemove.Add(i);
        }

        while (numsToRemove.Count > 0)
        {
            luckyNumbers.RemoveAt(numsToRemove[numsToRemove.Count - 1]);
            numsToRemove.RemoveAt(numsToRemove.Count - 1);
        }

        step = luckyNumbers[stepIndex++];
    }

    return luckyNumbers.ToArray();
}

Der Code zum Testen war:

public static void Main()
{
    Test(10, new Int32[] { 1, 3, 7, 9 });
    Test(20, new Int32[] { 1, 3, 7, 9, 13, 15 });
    Test(30, new Int32[] { 1, 3, 7, 9, 13, 15, 21, 25 });
    Test(50, new Int32[] { 1, 3, 7, 9, 13, 15, 21, 25, 31, 33, 37, 43, 49 });
    Test(63, new Int32[] { 1, 3, 7, 9, 13, 15, 21, 25, 31, 33, 37, 43, 49, 51, 63 });
    Test(77, new Int32[] { 1, 3, 7, 9, 13, 15, 21, 25, 31, 33, 37, 43, 49, 51, 63, 67, 69, 73, 75 });
    Test(100, new Int32[] { 1, 3, 7, 9, 13, 15, 21, 25, 31, 33, 37, 43, 49, 51, 63, 67, 69, 73, 75, 79, 87, 93, 99 });
    Test(120, new Int32[] { 1, 3, 7, 9, 13, 15, 21, 25, 31, 33, 37, 43, 49, 51, 63, 67, 69, 73, 75, 79, 87, 93, 99, 105, 111, 115 });

    Console.ReadKey();
}

private static void Test(Int32 num, Int32[] expected)
{
    var numbers = GetLuckyNumbers(num);

    var success = numbers.Length == expected.Length;

    if (success)
    {
        for (var i = 0; i < numbers.Length; i++)
        {
            if (numbers[i] != expected[i])
            {
                success = false;
                break;
            }
        }
    }

    Console.WriteLine(success ? "Success!" : "Failed!");

    Console.Write("Expected: ");
    for (var i = 0; i < expected.Length; i++)
    {
        Console.Write(expected[i]);
        Console.Write(" ");
    }

    Console.WriteLine();

    Console.Write("Computed: ");
    for (var i = 0; i < numbers.Length; i++)
    {
        Console.Write(numbers[i]);
        Console.Write(" ");
    }

    Console.WriteLine();
    Console.WriteLine();
}

Gruß, Christian.

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
1.002 Beiträge seit 2007
vor 13 Jahren

Hallo Kaji,

da du zuerst abgegeben hast und deine Lösung funktioniert, bist du mit der nächsten Aufgabe dran!

Hallo Schamese,

vielleicht tröstet es dich, wenn ich dir sage, dass deine Lösung die schönere gewesen wäre 😃.

m0rius

Mein Blog: blog.mariusschulz.com
Hochwertige Malerarbeiten in Magdeburg und Umgebung: M'Decor, Ihr Maler für Magdeburg

K
593 Beiträge seit 2007
vor 13 Jahren

Hallo Leute,

da ich nicht wirklich fit bin und mir nichts so recht kurzes kleines einfällt wäre es schön wenn jemand anderes eine Aufgabe stellen könnte 😃

Gruß Kaji

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo Kaji,

ich hätte da was. 😃

Hallo zusammen,

ist eine eher kleine Spielerei ohne praktischen Nutzen, aber dafür tricky zu programmieren.

Die Aufgabe:

Schreibt ein Windows Forms Programm, das unter Verwendung von Array.Sort (T [], IComparer<T>) oder Array.Sort (T [], Comparison<T>) eine vorgegebene Liste von Strings sortiert und am Ende in einer ListBox anzeigt, ohne dass dabei je das GUI blockiert und ohne dass es dabei "ungültige threadübergreifende Zugriffe" gibt.

Wo ist der Witz, werdet ihr euch fragen. Man kann doch einfach das Array.Sort in einem Thread starten und schon blockiert das GUI nicht. Und zum Anzeigen der sortierten Liste verwendet man Control.Invoke/BeginInvoke und schon hat man auch die "ungültigen threadübergreifenden Zugriffe" vermieden.

[FAQ] Warum blockiert mein GUI?
[FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke)

Ok, stellt euch vor, die Liste von Strings enthält die sieben Eissorten Vanille, Schokolade, Stracciatella, Erdbeer, Zitrone, Kirsch und Schlumpf. Und die Liste soll nach dem Geschmack des Benutzers aufsteigend sortiert werden, die sortierte Liste fängt mit dem am wenigsten gemochten Eis an und hört mit dem leckersten auf.

Der Comparer muss also immer irgendwie den Benutzer fragen, welche von zwei Eissorten er leckerer findet. Dazu sollen die beiden gerade zu vergleichenden Eissorten in zwei Textboxen angezeigt werden und der Benutzer klickt auf die TextBox, in der das Eis steht, das er leckerer findet als das andere. Er muss sich entscheiden, "gleich lecker" ist nicht möglich.

Die Kommunikation zwischen dem Comparer und den GUI hinzubekommen ohne, dass das GUI blockiert, ist also die eigentliche Herausforderung der Aufgabe. Nicht ganz einfach, aber machbar. Viel Spaß dabei.

herbivore

PS: Damit die Länge des Codes nicht unnötig aufgebläht wird, muss sich der Benutzer kooperativ verhalten. Er muss also konsistent antworten. Was andersherum gesagt heißt, er darf keine widersprüchlichen Eingaben machen: Eine widersprüchliche Eingabe wäre, wenn er sagt: Vanille ist leckerer als Schokolade, Schokolade ist lecker Stracciatella und Stracciatella ist lecker als Vanille. In einem robusten Programm müsste man solche widersprüchlichen Eingaben verhindern oder abfangen, aber das würde wie gesagt den Code sehr aufblähen und soll daher nicht Teil der Aufgabe sein.

PPS: Bitte gebt minimalen, aber vollständigen Code an. Wenn man den Designer benutzt, erfordert das vermutlich etwas Nacharbeit, die ich aber als Teil der Aufgabe ansehe. Nur wenn man den geposteten Code 1:1 in eine Datei kopieren und (mit csc) übersetzen kann, gilt die Lösung. 😃

390 Beiträge seit 2008
vor 13 Jahren

Hallo herbivore

Hier meine Lösung!


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows.Forms;

namespace Mcs
{
  class Program
  {
    [STAThread]
    static void Main(string[] args)
    {
      Application.EnableVisualStyles();

      Application.Run(new SortForm());
    }
  }

  class SortForm : Form
  {
    private ListBox sortedListListBox = new ListBox();
    private TextBox firstCompareTextBox = new TextBox();
    private TextBox secondCompareTextBox = new TextBox();
    private Button startSortingButton = new Button();
    private GroupBox compareGroupBox = new GroupBox();

    public SortForm()
    {
      InitializeComponent();
      sortedListListBox.Items.AddRange("Vanille, Schokolade, Stracciatella, Erdbeer, Zitrone, Kirsch und Schlumpf".Split(new string[] { ", ", " und " }, StringSplitOptions.RemoveEmptyEntries));
    }

    private void InitializeComponent()
    {
      //SortForm
      Height = 280;
      Width = 229;
      FormBorderStyle = FormBorderStyle.Fixed3D;
      Text = "UISort";
      MaximizeBox = false;

      //sortedListComboBox
      sortedListListBox.Top = 10;
      sortedListListBox.Left = 10;
      sortedListListBox.Width = 200;
      sortedListListBox.Height = 200;

      Controls.Add(sortedListListBox);

      //startSortingButton
      startSortingButton.Top = 215;
      startSortingButton.Width = 75;
      startSortingButton.Height = 23;
      startSortingButton.Left = 10;
      startSortingButton.Text = "Sort !1elf";
      startSortingButton.Click += StartSortingButton_Click;

      Controls.Add(startSortingButton);
    }

    private void PrepareComparisonControls()
    {
      //SortForm
      Width += 160;

      //compareGroupBox
      compareGroupBox.Top = 10;
      compareGroupBox.Left = 220;
      compareGroupBox.Width = 150;
      compareGroupBox.Height = 200;
      compareGroupBox.Text = "Compare";

      Controls.Add(compareGroupBox);

      //firstCompareTextBox
      firstCompareTextBox.Top = 15;
      firstCompareTextBox.Left = 10;
      firstCompareTextBox.Width = 100;
      firstCompareTextBox.Height = 23;
      firstCompareTextBox.ReadOnly = true;
      compareGroupBox.Controls.Add(firstCompareTextBox);

      //secondCompareTextBox
      secondCompareTextBox.Top = 43;
      secondCompareTextBox.Left = 10;
      secondCompareTextBox.Width = 100;
      secondCompareTextBox.Height = 23;
      secondCompareTextBox.ReadOnly = true;

      compareGroupBox.Controls.Add(secondCompareTextBox);
    }

    private void RemoveComparisonControls()
    {
      if (InvokeRequired)
      {
        Invoke(new Action(RemoveComparisonControls));
      }
      else
      {
        //Reset
        Width -= 160;
        Controls.Remove(compareGroupBox);
        compareGroupBox.Controls.Remove(firstCompareTextBox);
        compareGroupBox.Controls.Remove(secondCompareTextBox);
      }
    }

    private void StartSorting()
    {
      string[] elements = sortedListListBox.Items.Cast<String>().ToArray();
      UIComparer comparer = new UIComparer();

      comparer.ComparisonRequired += ComparisonRequired;

      ThreadPool.QueueUserWorkItem(
        new WaitCallback(o =>
      {
        Array.Sort<String>(elements, comparer);
        DisplaySortedResult(elements);
        RemoveComparisonControls();
      }), null);
    }

    private void DisplayComparison(CompareEventArgs e)
    {
      if (InvokeRequired)
      {
        Invoke(new Action<CompareEventArgs>(DisplayComparison), e);
      }
      else
      {
        firstCompareTextBox.Text = e.X;
        secondCompareTextBox.Text = e.Y;

        firstCompareTextBox.Click += (o, ea) => e.Chosen = e.X;
        secondCompareTextBox.Click += (o, ea) => e.Chosen = e.Y;
      }
    }

    private void DisplaySortedResult(String[] elements)
    {
      if (InvokeRequired)
      {
        Invoke(new Action<String[]>(DisplaySortedResult), ((Object)elements));
      }
      else
      {
        sortedListListBox.Items.Clear();
        sortedListListBox.Items.AddRange(elements);
      }
    }

    #region Events

    private void StartSortingButton_Click(Object sender, EventArgs e)
    {
      PrepareComparisonControls();
      StartSorting();
    }

    private void ComparisonRequired(Object sender, CompareEventArgs e)
    {
      DisplayComparison(e);
    }

    #endregion
  }

  class UIComparer : IComparer<String>
  {

    #region IComparer<string> Member

    public int Compare(String x, String y)
    {
      if (x == y)
        return 0;

      CompareEventArgs e = new CompareEventArgs(x, y);

      if (ComparisonRequired != null)
        ComparisonRequired.Invoke(this, e);

      e.WaitForUserInput();

      if (e.Chosen == x)
      {
        return 1337;
      }
      else
      {
        return -1337;
      }
    }

    #endregion

    public event EventHandler<CompareEventArgs> ComparisonRequired;
  }

  class CompareEventArgs : EventArgs
  {
    private String chosen = string.Empty;
    private AutoResetEvent waiter;

    public CompareEventArgs(String x, String y)
    {
      X = x;
      Y = y;
      waiter = new AutoResetEvent(false);
    }

    public void WaitForUserInput()
    {
      waiter.WaitOne();
    }

    public String X { get; set; }
    public String Y { get; set; }

    public String Chosen
    {
      get
      {
        return chosen;
      }
      set
      {
        chosen = value;
        waiter.Set();
      }
    }
  }
}


using Skill

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo edsplash,

zwei Kleinigkeiten habe(hatte) ich gefunden:

Die eine, nämlich dass dem Benutzer Fragen der Art "Kirsch" vs "Kirsch" gestellt, hast du mittlerweile selber bemerkt und behoben.

Die andere ist, dass für die TextBoxen immer neue EventHandler registriert werden, ohne dass die alten deregistriert werden. Das führt mal abgesehen von dem bei steigender Listenlänge quadratischen Aufwand inbesondere dazu, dass die EventArgs die ganze Zeit nicht freigegeben werden.

Ansonsten funktioniert die Lösung genau so, wie ich mir das vorgestellt hatte, also inbesondere mit dem AutoResetEvent, durch den der Sort-Thread wartet, bis der Benutzer sich entschieden hat.

Du bist mit der nächsten Aufgabe dran.

herbivore

390 Beiträge seit 2008
vor 13 Jahren

Hallo herbivore

Das mit dem Kirsch vs Kirsch halte ich nach wie vor für einen Schönheitsfehler, da der Quicksort Algorithmus ja sowieso nicht weiss, was er machen soll wenn zwei Werte gleich gross sind. Es ändert sich da Funktional also nichts. Habs aber trotzdem nachgebessert. 😉

Die Click Events hätte man allerdings generischer behandeln können, aber da hier gerne Quick&Dirty Lösungen präsentiert werden, bzw. das im ersten Beitrag erwähnt wird, ist das wohl auch nicht so Schlimm. 😉

Meine Aufgabe kommt dann Morgen. 😃

Gruss

using Skill

390 Beiträge seit 2008
vor 13 Jahren

Hallo Zusammen

Meine Aufgabe:

Ziel ist es ein Interface IMap<TLeft, TRight> zu implementieren: ein IMap Objekt stellt quasi eine 1:1 Verknüpfung, bzw. eine eindeutige Zuordnung von Objekten verschiedenen Typs dar. Sowohl auf der linken, wie auf der rechten Seite darf nicht zweimal dasselbe Objekt vorkommen. Elemente der rechten Seite können dabei mit dem entsprechenden Element der linken Seite aus dem IMap Objekt geladen/beschrieben werden und umgekehrt.

Man sollte dabei sofern möglich vermeiden auf .NET Klassen wie Dictionary<T>, HashSet<T> zurück zu greifen. (Wäre ja langweilig 😃)


  interface IMap<TLeft, TRight>
  {
    /// <summary>
    /// Fügt der Map eine Zuordnung hinzu
    /// </summary>
    void Add(TLeft leftItem, TRight rightItem);

    /// <summary>
    /// Liest ein Element auf der rechten Seite der Map aus oder setzt dieses
    /// </summary>
    /// <param name="item">Ein Element auf der linken Seite, welches als Schlüssel dient</param>
    TRight this[TLeft item] { get; set; }

    /// <summary>
    /// Liest ein Element auf der linken Seite der Map aus oder setzt dieses
    /// </summary>
    /// <param name="item">Ein Element auf der rechten Seite, welches als Schlüssel dient</param>
    TLeft this[TRight item] { get; set; }

    /// <summary>
    /// Gibt an, ob auf der linken Seite ein spezifisches Element vorhanden ist
    /// </summary>
    bool Contains(TLeft item);

    /// <summary>
    /// Gibt an, ob auf der rechten Seite ein spezifisches Element vorhanden ist
    /// </summary>
    bool Contains(TRight item);

    /// <summary>
    /// Löscht eine Zuordnung
    /// </summary>
    /// <param name="item">Ein Element auf der linken Seite, welches als Schlüssel dient</param>
    bool Remove(TLeft item);

    /// <summary>
    /// Löscht eine Zuordnung
    /// </summary>
    /// <param name="item">Ein Element auf der rechten Seite, welches als Schlüssel dient</param>
    bool Remove(TRight item);

    /// <summary>
    /// Löscht alle Zuordnungen
    /// </summary>
    void Clear();

    /// <summary>
    /// Gibt die Anzahl der vorhandenen Zuordnungen zurück
    /// </summary>
    int Count { get; }
  }

Gruss

using Skill

2.891 Beiträge seit 2004
vor 13 Jahren

stellt quasi eine 1:1 Verknüpfung, bzw. eine eindeutige Zuordnung

*klugscheiß* Du meinst eineindeutig!? Als bijektiv (rechtseindeutig/surjektiv und linkseindeutig/injektiv).

2.298 Beiträge seit 2010
vor 13 Jahren

Um Exceptionhandling hab ich mich nun aber nicht gekümmert. War auch wenn ich mich nicht verlesen hab kein Teil der Aufgabe. 😃

Hoffe das passt so.


class IMapClass : IMap<String, Object>
{
	#region IMap<T,T> Member

	List<String> _leftList = new List<String>();
	List<Object> _rightList = new List<object>();

	public void Add(String leftItem, Object rightItem)
	{
		if (!this._leftList.Contains(leftItem) 
			&& !this._rightList.Contains(rightItem))
		{
			this._leftList.Add(leftItem);
			this._rightList.Add(rightItem);
		}
		else
		{
			throw new InvalidOperationException("Zuordnung nicht möglich, ein Objekt darf nur einmal vorkommen.");
		}
	}

	public Object this[String item]
	{
		get
		{
			return this._rightList[_leftList.IndexOf(item)];			
		}
		set
		{
                        if(!_rightList.Contains(item))
				this._rightList[_leftList.IndexOf(item)] = value;
                        else 
                                throw new InvalidOperationException("Das Objekt existiert bereits bei einer anderen Zuordnung.");
		}
	}
	public String this[Object item]
	{
		get
		{
			return this._leftList[_rightList.IndexOf(item)];
		}
		set
		{
                        if(!_leftList.Contains(item)=
				this._leftList[_rightList.IndexOf(item)] = value;
                        else 
                                throw new InvalidOperationException("Das Objekt existiert bereits bei einer anderen Zuordnung.");
		}
	}

	public bool Contains(String item)
	{
		return _leftList.Contains(item);
	}

	public bool Contains(Object item)
	{
		return _rightList.Contains(item);
	}

	public bool Remove(String item)
	{			
		return this._rightList.Remove(_rightList[_leftList.IndexOf(item)]) && this._leftList.Remove(item);			
	}

	public bool Remove(Object item)
	{
		return this._leftList.Remove(_leftList[_rightList.IndexOf(item)]) && this._rightList.Remove(item);
	}

	public void Clear()
	{
		this._leftList.Clear();
		this._rightList.Clear();
	}

	public int Count
	{
		get { return this._leftList.Count; }
	}

	#endregion
}


Für Meinungen bin ich natürlich offen.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo inflames2k,

Um Exceptionhandling hab ich mich nun aber nicht gekümmert. War auch wenn ich mich nicht verlesen hab kein Teil der Aufgabe. 😃

nö, aber es war verlangt, dass Objekte nicht mehrfach vorkommen können. Das hast du zwar bei Add berücksichtigt, aber an anderen Stellen nicht.

Hallo edsplash,

wo ich eh gerade einen Beitrag schreibe:

Man sollte dabei sofern möglich vermeiden auf .NET Klassen wie Dictionary<T>, HashSet<T> zurück zu greifen.

Da hast du ja was schönes erreicht. inflames2k hat List <T>genommen (und wenn auch das nicht erlaubt gewesen wäre, dann vermutlich Array, irgendworauf wird man ja sinnvollerweise aufbauen). Dadurch ist deine Bedingung formal erfüllt. Aber was hast du erreicht? Der Code ist auch nicht länger oder komplizierter als wenn man Dictionary<> verwendet hätte, aber um Größenordnungen weniger performant.

herbivore

2.298 Beiträge seit 2010
vor 13 Jahren

Stimmt, gab ja noch die Zuordnung per Objekt[key]. - Hab es im Code angepasst.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo inflames2k,

dein Code trifft es immer noch nicht. Du musst ja auf jeden Fall auch value prüfen. Außerdem muss es erlaubt sein, einem Objekt das bereits zugeordnete Objekt erneut zuzuordnen. Überlegt dir nochmal genau, welche Fälle alle auftreten können.

BTW: warum hast du die Klasse denn für konkrete Typen implementiert? Statt class IMapClass : IMap<String, Object> wäre Map<TLeft, TRight> : IMap<TLeft, TRight> deutlich sinnvoller gewesen (mal ganz abgesehen davon, dass Klassennamen keinen Präfix haben sollten, erst recht nicht den Präfix I).

herbivore

2.298 Beiträge seit 2010
vor 13 Jahren

Naja, stimmt so wohl auch wieder.

value hatte ich im Visual Studio geprüft wohl aber hier in der Änderung vergessen dies zu ändern.

Das mit der neu Zuordnung des selben Objektes stimmt so natürlich auch wieder. - Den Fall hatte ich nicht bedacht.

Konkrete Typen habe ich genommen, nachdem mein Compiler begann mir vorwürfe zu machen.

Nun aber die Volle und nun denk ich richtigere Variante:


	class Map<TLeft, TRight> : IMap<TLeft, TRight>
	{
		#region IMap<TLeft,TRight> Member

		List<TLeft> _leftList = new List<TLeft>();
		List<TRight> _rightList = new List<TRight>();

		public void Add(TLeft leftItem, TRight rightItem)
		{
			if (!_leftList.Contains(leftItem) && !_rightList.Contains(rightItem))
			{
				_leftList.Add(leftItem);
				_rightList.Add(rightItem);
			}
			else
				throw new InvalidOperationException("Ein Objekt kann nicht mehrfach zugeordnet werden.");
		}

		public TRight this[TLeft item]
		{
			get
			{
				return _rightList[_leftList.IndexOf(item)];
			}
			set
			{
				if(!_rightList.Contains(value) || _rightList[_leftList.IndexOf(item)].Equals(value))
					_rightList[_leftList.IndexOf(item)] = value;
				else throw new InvalidOperationException("Objekt ist bereits in der Zuordnung vorhanden.");
			}
		}

		public TLeft this[TRight item]
		{
			get
			{
				return _leftList[_rightList.IndexOf(item)];
			}
			set
			{
				if (!_leftList.Contains(value) || _leftList[_rightList.IndexOf(item)].Equals(value))
					_leftList[_rightList.IndexOf(item)] = value;
				else throw new InvalidOperationException("Objekt ist bereits in der Zuordnung vorhanden.");
			}
		}

		public bool Contains(TLeft item)
		{
			return _leftList.Contains(item);
		}

		public bool Contains(TRight item)
		{
			return _rightList.Contains(item);
		}

		public bool Remove(TLeft item)
		{
			return _rightList.Remove(_rightList[_leftList.IndexOf(item)]) && _leftList.Remove(item);
		}

		public bool Remove(TRight item)
		{
			return _leftList.Remove(_leftList[_rightList.IndexOf(item)]) && _rightList.Remove(item);
		}

		public void Clear()
		{
			_leftList.Clear();
			_rightList.Clear();
		}

		public int Count
		{
			get { return _leftList.Count; }
		}

		#endregion
	}

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

390 Beiträge seit 2008
vor 13 Jahren

Hallo herbivore

Da hast du ja was schönes erreicht. inflames2k hat List <T>genommen (und wenn auch das nicht erlaubt gewesen wäre, dann vermutlich Array, irgendworauf wird man ja sinnvollerweise aufbauen). Dadurch ist deine Bedingung formal erfüllt. Aber was hast du erreicht? Der Code ist auch nicht länger oder komplizierter als wenn man Dictionary<> verwendet hätte, aber um Größenordnungen weniger performant.

Hehe, ich hatte gedacht, jemand würde es ohne Dictionary ähnlich performant hinkriegen 😉 Mit dem Dictionary wird der Code allerdings weniger komplex, vorallem wenn man zwei davon verwendet -> Dictionary<TLeft, TRight> und Dictionar<TRight, TLeft>.

Hallo inflames2k

Deine Lösung scheint korrekt zu sein!

using Skill

795 Beiträge seit 2006
vor 13 Jahren

Gnahh... Schon wieder zu langsam....

Gefragt war aber auch, die Implementierung ohne die Generischen Listen / Dictionaries aus System.Collections.Generic.

Hier ist meine Lösung:

public class Map<TLeft, TRight> : IMap<TLeft, TRight>
{
	private TLeft[] m_Left = null;
	private TRight[] m_Right = null;
	private Boolean[] m_Used = null;
	private Int32 m_Count = 0;
	
	public Map()
	    : this(100) { }
	
	public Map(Int32 startCapacity) {
		m_Left = new TLeft[startCapacity];
		m_Right = new TRight[startCapacity];
		m_Used = new Boolean[startCapacity];
	}
	
	/// <summary>
    /// Fügt der Map eine Zuordnung hinzu
    /// </summary>
    public void Add(TLeft leftItem, TRight rightItem) {
        if (Contains(leftItem)) {
            throw new ArgumentException("Item already added!", "leftItem");
        }
        
        if (Contains(rightItem)) {
            throw new ArgumentException("Item already added!", "rightItem");
        }
        
        Int32 index = GetNextFreeIndex();
        
        m_Used[index] = true;
        m_Left[index] = leftItem;
        m_Right[index] = rightItem;
        m_Count++;
	}

    /// <summary>
    /// Liest ein Element auf der rechten Seite der Map aus oder setzt dieses
    /// </summary>
    /// <param name="item">Ein Element auf der linken Seite, welches als Schlüssel dient</param>
    public TRight this[TLeft item] {
        get {
            Int32 idx = Find(item);
            
            if (idx == -1) {
                throw new KeyNotFoundException("Item not found!");
            }
            
            return m_Right[idx];
        }
        set {
            if (Contains(value)) {
                throw new ArgumentException("Item already added!", "value");
            }
            
            Int32 idx = Find(item);
            
            if (idx == -1) {
                Add(item, value);
            } else {
                m_Right[idx] = value;
            }
        }
    }

    /// <summary>
    /// Liest ein Element auf der linken Seite der Map aus oder setzt dieses
    /// </summary>
    /// <param name="item">Ein Element auf der rechten Seite, welches als Schlüssel dient</param>
    public TLeft this[TRight item] {
        get {
            Int32 idx = Find(item);
            
            if (idx == -1) {
                throw new KeyNotFoundException("Item not found!");
            }
            
            return m_Left[idx];
        }
        set {
            if (Contains(value)) {
                throw new ArgumentException("Item already added!", "value");
            }
            
            Int32 idx = Find(item);
            
            if (idx == -1) {
                Add(value, item);
            } else {
                m_Left[idx] = value;
            }
        }
    }

    /// <summary>
    /// Gibt an, ob auf der linken Seite ein spezifisches Element vorhanden ist
    /// </summary>
    public Boolean Contains(TLeft item) {
        return Find(item) != -1;
    }

    /// <summary>
    /// Gibt an, ob auf der rechten Seite ein spezifisches Element vorhanden ist
    /// </summary>
    public Boolean Contains(TRight item) {
        return Find(item) != -1;
    }

    /// <summary>
    /// Löscht eine Zuordnung
    /// </summary>
    /// <param name="item">Ein Element auf der linken Seite, welches als Schlüssel dient</param>
    public Boolean Remove(TLeft item) {
        return Remove(Find(item));
    }

    /// <summary>
    /// Löscht eine Zuordnung
    /// </summary>
    /// <param name="item">Ein Element auf der rechten Seite, welches als Schlüssel dient</param>
    public Boolean Remove(TRight item) {
        return Remove(Find(item));
    }

    /// <summary>
    /// Löscht alle Zuordnungen
    /// </summary>
    public void Clear() {
        m_Used = new Boolean[m_Used.Length];
        m_Left = new TLeft[m_Left.Length];
        m_Right = new TRight[m_Right.Length];
    }

    /// <summary>
    /// Gibt die Anzahl der vorhandenen Zuordnungen zurück
    /// </summary>
    public Int32 Count {
        get {
            return m_Count;
        }
    }
    
    private Boolean Remove(Int32 idx) {
        if (idx < 0 || idx > m_Used.Length || !m_Used[idx]) {
            return false;
        }
        
        m_Used[idx] = false;
        m_Left[idx] = default(TLeft);
        m_Right[idx] = default(TRight);
        
        m_Count--;
        
        return true;
    }
    
    private Int32 GetNextFreeIndex() {
        for (Int32 i = 0; i < m_Used.Length; i++) {
            if (!m_Used[i]) {
                return i;
            }
        }
        
        EnlargeArrays();
        
        return GetNextFreeIndex();
    }
    
    private void EnlargeArrays() {
        Boolean[] tmpUsed = m_Used;
        TLeft[] tmpLeft = m_Left;
        TRight[] tmpRight = m_Right;
        
        m_Used = new Boolean[tmpUsed.Length * 2];
        m_Left = new TLeft[tmpLeft.Length * 2];
        m_Right = new TRight[tmpRight.Length * 2];
        
        tmpUsed.CopyTo(m_Used, 0);
        tmpLeft.CopyTo(m_Left, 0);
        tmpRight.CopyTo(m_Right, 0);
    }
    
    private Int32 Find(TLeft item) {
        for (Int32 i = 0; i < m_Used.Length; i++) {
            if (m_Used[i] && m_Left[i].Equals(item)) {
                return i;
            }
        }
        
        return -1;
    }
    
    private Int32 Find(TRight item) {
        for (Int32 i = 0; i < m_Used.Length; i++) {
            if (m_Used[i] && m_Right[i].Equals(item)) {
                return i;
            }
        }
        
        return -1;
    }
}

Gruß, Christian.

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
2.298 Beiträge seit 2010
vor 13 Jahren

Gut, eine Aufgabe gibt es sobald mir etwas eingefallen ist. - Hatte zwar gerade einen Gedanken aber den müsst ich erstmal selbst für mich zusammen fassen.

Esseidenn dir fällt eine gute Aufgabe ein Schamese. 😃 - Dann darfst du sie auch gern stellen.

Vermieden werden sollten Dictionary und HashSet, nicht alle Generics, aufgrund der Funktionalitäten die vom Dictionary Beispielsweise schon gegeben sind.

Wobei das nun auch unrelevant wäre. - Wie herbivore sagte, performant ist das nicht.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

795 Beiträge seit 2006
vor 13 Jahren

Hi @ All.

Nach Absprache mit inflames2k darf ich nun eine Aufgabe stellen.

Entwickle einen kleinen Parser & Interpreter für simple mathematische Formeln.
Er soll folgendes beherrschen:*Addieren, Subtrahieren, Multiplizieren und Dividieren von Integer-Literalen. *Terme sollen mit Klammern zusammengefasst werden können. *Der Interpreter soll das Ergebnis des eingegebenen Terms berechnen. *Variablen, Funktionen und symbolische Konstanten brauchen nicht eingebaut werden.

Natürlich soll man seinen eigenen Parser/Interpreter schreiben. Parser-Generatoren, Vorgefertigte Interpreter, Scripting-Engines, Regex etc. sind nicht erlaubt. Ebenso gilt der Parser für mathematische Formeln nicht und darf auch nicht als Vorlage benutzt werden.

Gruß & Viel Spaß,
Christian.

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
795 Beiträge seit 2006
vor 13 Jahren

Hi @ All.

Ich habe auf den Rat von herbivore die Aufgabenstellung noch einmal angepasst.

Gruß, Christian.

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
3.170 Beiträge seit 2006
vor 13 Jahren

Hallo,

ich glaub das müsste es bringen:

using System;
using System.Collections.Generic;

namespace TestApp
{
  class Program
  {
    static double Calc(string input)
    {
// die folgende Zeile wiede4r einkommentieren um das Verfahren zu sehen
//    Console.WriteLine(input); 
      input = input.Trim();
      if(input.Length > 0)
      {
        int open = input.LastIndexOf('(');
        if(open >= 0)
        {
          int match = input.IndexOf(')',open);
          if(match > open)
          {
            double partRes = Calc(input.Substring(open+1, match-open-1));
            if(partRes != double.NaN)
            {
              return Calc(input.Remove(open, match-open+1).Insert(open, partRes.ToString()));
            }
          }
        }
        else
        {
          if(char.IsDigit(input[input.Length-1]))
          {
            int pos = input.IndexOf('+');
            if(pos > 0)
            {
              return Calc(input.Substring(0,pos)) + Calc(input.Substring(pos+1));
            }
            pos = input.IndexOf('-');
            if((pos > 0) && (Char.IsDigit(input[pos-1]))) // <<< hier das IsDigit eingebaut
            {
              return Calc(input.Substring(0,pos)) - Calc(input.Substring(pos+1));
            }
            pos = input.IndexOf('*');
            if(pos > 0)
            {
              return Calc(input.Substring(0,pos)) * Calc(input.Substring(pos+1));
            }
            pos = input.IndexOf('/');
            if(pos > 0)
            {
              return Calc(input.Substring(0,pos)) / Calc(input.Substring(pos+1));
            }
            double result;
            if(double.TryParse(input, out result))
            {
              return result;
            }
          }
        }
      }
      return double.NaN;
    }
    
    public static void Main(string[] args)
    {
      do
      {
        Console.WriteLine("Term eingeben:");
        double result = Calc(Console.ReadLine());
        Console.WriteLine("Ergebnis: " + result.ToString());
        Console.WriteLine();
        Console.WriteLine("Leertaste zum Beenden, andere Taste zum Fortfahren");
      }while(Console.ReadKey().Key != ConsoleKey.Spacebar);
    }
  }
}

Edit: erlaubt sind nur die runden Klammern "()". Bei Fehleingaben müsste immer NaN zurückgegeben werden.

EDIT 2: Der Code hat nicht funktioniert, wenn innerhalb eines Klammerpaars eine negative Zahl herauskam. Ich habe die korrigierte Stelle oben markiert (Prüfung auf IsDigit vor einem '-')

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

Gelöschter Account
vor 13 Jahren

das da:

if(partRes != double.NaN)

ist immer true. also kannst du es auch gleich weglassen, was dir dann immernoch das problem übriglässt, das partRes noch NaN sein könnte. aber fehleingaben sind ja keine bedingung. ich wollte es nur mal erwähnt wissen 😃

gib mal "-(105/6+(-57))" ein 😉

3.170 Beiträge seit 2006
vor 13 Jahren

Hallo,

das da: if(partRes != double.NaN)ist immer true.

Wenn Du den von Dir genannten Term eingibst, und zusätzlich innerhalb des if mal eine Konsolenausgabe machst, siehst Du, dass Du damit fasch liegst.

Trotzdem hast Du natürlich recht: da ist noch ein Fehler drin. Kümmere mich bald darum -> keine Zeit jetzt.

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

Gelöschter Account
vor 13 Jahren

sry... nein. 😉

versuch mal:

            string nurUmOptimierungenZuVermeiden = Console.ReadLine();
            double d = double.NaN;
            if(d == double.NaN) Console.WriteLine("hier ist double ein NaN \n" + nurUmOptimierungenZuVermeiden);
3.170 Beiträge seit 2006
vor 13 Jahren

Hallo Jack30lena,

du hast natürlich recht 😉 habs jetzt auf double.IsNaN umgestellt, und das Problem mit "-(" dadurch behoben, dass es durch "-1*(" ersetzt wird:

using System;

namespace TestApp
{
  class Program
  {
    static double Calc(string input)
    {
      // die folgende Zeile wiede4r einkommentieren um das Verfahren zu sehen
      //    Console.WriteLine(input);
      input = input.Trim();
      if (input.Length > 0)
      {
        int open = input.LastIndexOf('(');
        if (open >= 0)
        {
          int match = input.IndexOf(')', open);
          if (match > open)
          {
            double partRes = Calc(input.Substring(open + 1, match - open - 1));
            // partRes = double.NaN;  <<< die böse Zeile (siehe Folgebeiträge)
            if (!double.IsNaN(partRes))
            {
              return Calc(input.Remove(open, match - open + 1).Insert(open, partRes.ToString()));
            }
          }
        }
        else if (char.IsDigit(input[input.Length - 1]))
        {
          int pos = input.IndexOf('+');
          if (pos > 0)
          {
            return Calc(input.Substring(0, pos)) + Calc(input.Substring(pos + 1));
          }
          pos = input.IndexOf('-');
          if ((pos > 0) && (Char.IsDigit(input[pos - 1]))) // <<< hier das IsDigit eingebaut
          {
            return Calc(input.Substring(0, pos)) - Calc(input.Substring(pos + 1));
          }
          pos = input.IndexOf('*');
          if (pos > 0)
          {
            return Calc(input.Substring(0, pos)) * Calc(input.Substring(pos + 1));
          }
          pos = input.IndexOf('/');
          if (pos > 0)
          {
            return Calc(input.Substring(0, pos)) / Calc(input.Substring(pos + 1));
          }
          double result;
          if (double.TryParse(input, out result))
          {
            return result;
          }
        }
      }
      return double.NaN;
    }

    static double Parse(String input) { 
      return Calc(input.Replace("-(", "-1*("));
    }

    public static void Main(string[] args)
    {
      do
      {
        Console.WriteLine("Term eingeben:");
        double result = Parse(Console.ReadLine());
        Console.WriteLine("Ergebnis: " + result.ToString());
        Console.WriteLine();
        Console.WriteLine("Leertaste zum Beenden, andere Taste zum Fortfahren");
      } while (Console.ReadKey().Key != ConsoleKey.Spacebar);
    }
  }
}

sonst noch irgenwelche Fehler?

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

Gelöschter Account
vor 13 Jahren

ich kann keine fehler mehr feststellen. wunderschöne lösung. respekt 😃

795 Beiträge seit 2006
vor 13 Jahren

Ich hab aber doch noch einen Fehler gefunden:

Gib mal (12+1)*2 ein... --> Heraus kommen sollte 26, aber es kommt NaN raus...

Gruß, Christian.

//EDIT Irgendie kommt bei mir immer wenn eine Klammer drin ist n. def. raus...

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
771 Beiträge seit 2009
vor 13 Jahren

Die Zeile


partRes = double.NaN;

ist wohl dafür verantwortlich (wahrscheinlich noch der Test wegen den NaN-Beiträgen vorher -)

3.170 Beiträge seit 2006
vor 13 Jahren

Hallo,

in der Tat ist es genau so wie Cat schrieb: Da ist mir 'ne Zeile vom Testen stehengeblieben. Ich editiere das gleich oben im Code und mache die Stelle dort auch kenntlich. Danach sollte aber alles laufen 😉
Die nächste Aufgabe gibt's dann heut abend.

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

795 Beiträge seit 2006
vor 13 Jahren

Hallo, MarsStein.

Stimmt. Wenn ich die Zeile Raus nehme, funktioniert's einwandfrei.

Wie JAck30lena schon gesagt hat:

ich kann keine fehler mehr feststellen. wunderschöne lösung. respekt 😃

Damit bist du dran!

Gruß, Christian.

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
3.170 Beiträge seit 2006
vor 13 Jahren

Hallo,

wunderschöne lösung. respekt 🙂

danke dafür und für den Hinweis wegen NaN 🙂
Rekursion mal von hinten nach vorn und von innen nach außen 😁 😁

Dann mal die nächste Aufgabe:
erstellt ein Programm, das eine vom Benutzer wählbare Anzahl von Kugeln nach dem Prinzip des Galtonbretts auf einzelne Fächer verteilt. Die Anzahl der Stufen des Bretts soll ebenfalls vom Benutzer gewählt werden können (denkt daran, daß es ein Fach mehr als Stufen gibt).
Die dabei erzeugte Verteilung soll in einem Balkendiagramm (1 Balken je Fach) sinnvoll skaliert* ausgegeben werden. Die Balken sollen mit der entsprechenden Trefferzahl beschriftet werden.
Die Aufgabe ist mit der Ausgabe von waagerechten Balken auf der Konsole erfüllt.

Gruß, MarsStein

*Edit: Sinnvoll skaliert bezieht sich auf sinnvolle Eingabebereiche (die nicht geprüft werden müssen), bei denen die Anzahl der Kugeln viel größer als die Anzahl der Fächer ist, und die maximale Anzahl der Fächer im Bereich der Zeilenzahl der Konsole liegt.
Ich erwarte aber, daß die maximale Länge der Balken mit steigender Anzahl an Fächern mitwächst, die Kurve also halbwegs mitskaliert wird.

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

6.862 Beiträge seit 2003
vor 13 Jahren

Hallo,

hier mal ne kurze Lösung von mir weil ich auch mal ne Aufgabe stellen möchte 😃


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace Galton {
    class Program {
        static void Main(string[] args) {

            Console.Write("Geben Sie die Anzahl der Stufen an: ");
            var stufen = Convert.ToInt32(Console.ReadLine());
            Console.Write("Geben sie die Anzahl der Kugeln an: ");
            var kugeln = Convert.ToInt32(Console.ReadLine());

            Galton g = new Galton(stufen);
            var bins = g.DropBalls(kugeln);

            PrintResults(3, bins);

            Console.ReadLine();
        }

        static void PrintResults(int startY, int[] bins) {

            // Skalierungsfaktor anhand der größten Kugelmenge berechnen
            var scale = 75.0 / bins.Max();

            for (int i = 0; i < bins.Length - 1; i++) {
                // Anzahl zu druckender Zeichen bestimmen
                var end = (int)(bins[i] * scale);

                for (int j = 0; j < end; j++) {
                    Console.SetCursorPosition(j, startY + i*2);
                    Console.Write("x");
                }

                // Noch die Anzahl der Kugeln an den Anfang schreiben
                Console.SetCursorPosition(0, startY + i*2);
                Console.Write(String.Format("{0} ",bins[i]));
            }
        }
    }

    public class Galton {
        List<double> factors = new List<double>();
        Random rand = new Random();
        double sum;

        public Galton(int level) {
            Func<Decimal, Decimal> factorial = null;
            Func<Decimal, Decimal, Decimal> binom = null;

            // Fakultät
            factorial = (n) => (n == 0) ? 1 : n * factorial(n - 1);
            // Binomialkoeffizenten
            binom = (n, k) => factorial(n) / (factorial(k) * factorial(n - k));

            sum = Math.Pow(2.0, level);

            foreach (var i in Enumerable.Range(0, level + 1)) {
                factors.Add(Convert.ToDouble(binom(level, i)));
            }

            // Summen für jedes Fach aufaddieren, siehe DropBalls für warum
            factors = factors.Aggregate(
                new List<double>() { 0 },
                (acc, item) => {
                    acc.Add(acc[acc.Count - 1] + item);
                    return acc;
                }
                , (acc) => acc).ToList();
        }

        public int[] DropBalls(int balls) {
            // Fächer
            int[] bins = new int[factors.Count];

            // für jeden gezogenen Ball...
            for (int i = 0; i < balls; i++) {
                // Zufallszahl erzeugen
                var num = rand.NextDouble()*sum;
                // Fächersummen durchgehen wenn Zufallszahl kleiner als Summe, ist die Kugel ins vorherige Fach gefallen
                for (int j = 0; j < factors.Count; j++) {
                    if (num < factors[j]) {
                        bins[j-1]++;
                        break;
                    }
                }
            };
            return bins;
        }
    }
}

*Edit: Sinnvoll skaliert bezieht sich auf sinnvolle Eingabebereiche (die nicht geprüft werden müssen), bei denen die Anzahl der Kugeln viel größer als die Anzahl der Fächer ist, und die maximale Anzahl der Fächer im Bereich der Zeilenzahl der Konsole liegt.
Ich erwarte aber, daß die maximale Länge der Balken mit steigender Anzahl an Fächern mitwächst, die Kurve also halbwegs mitskaliert wird.

Ich erfülle diese Erwartung mit Absicht nicht - ob du die Aufgabe als gelöst erklärst ist dir überlassen. Der Grund ist einfach der, dass ich darin keinen Sinn sehe. Die Zeichenzahl in der Konsole pro Zeile ist so gering das man eh schon kaum gescheit skalieren kann. Wenn man bei wenigen Fächern die Balken kürzer macht, erkennt man bei Angabe der Kugeln in den Fächern gar nichts mehr.

Baka wa shinanakya naoranai.

Mein XING Profil.

3.170 Beiträge seit 2006
vor 13 Jahren

Hallo talla,

da die Skalierung ausdrücklich gefordert war, kann ich Deine Lösung leider so nicht durchgehen lassen 🙁
Diese eine Mutiplikation wirst Du schon noch einbauen müssen, wenn Du die nächste Aufgabe stellen möchtest...
Ich habe auch einen Grund dafür: Wenn die Balken an der Zeilenzahl skaliert werden, sind die Kurven von der Form her vergleichbarer, und die Gleichverteilung bei verschiedenen Eingaben läßt sich besser erkennen. Als Beispiel mal die Ausgabe meiner eigenen Lösung, ich denke das veranschaulicht das etwas:

Galtonbrett Simulation

Stufen: 8
Kugeln: 10000
  35 -
 297 -
1134 ----
2126 --------
2701 ---------
2252 --------
1088 ----
 321 --
  46 -

Stufen: 12
Kugeln: 100000
   20 -
  264 -
 1627 -
 5466 ----
12097 -------
19259 ------------
22551 -------------
19199 ------------
12198 --------
 5361 ----
 1630 -
  299 -
   29 -

Stufen: 16
Kugeln: 1000000
    20 -
   228 -
  1835 -
  8443 -
 27608 ---
 66411 ------
122927 -----------
174475 ----------------
196019 -----------------
175201 ----------------
121609 -----------
 66909 ------
 27857 ---
  8379 -
  1802 -
   255 -
    22 -

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

6.862 Beiträge seit 2003
vor 13 Jahren

Hallo,

hier die geänderte Ausgabefunktion:

        static void PrintResults(int startY, int[] bins) {
            var max = Convert.ToDouble(bins.Max());
            var baseChars = 10.0;
            // skalieren mit dem 10er Logarithmus des Maximums
            var log = (int)Math.Log10(max);
            for (int i = 0; i < log; i++) {
                baseChars += 5.0;
            }

            // Skalierungsfaktor anhand der größten Kugelmenge berechnen
            var scale = baseChars / bins.Max();
            var offset = 10;

            for (int i = 0; i < bins.Length - 1; i++) {
                // Anzahl zu druckender Zeichen bestimmen
                var end = (int)(bins[i] * scale);

                for (int j = 0; j < end; j++) {
                    Console.SetCursorPosition(offset + j, startY + i*2);
                    Console.Write("x");
                }

                // Noch die Anzahl der Kugeln an den Anfang schreiben
                Console.SetCursorPosition(0, startY + i*2);
                Console.Write(bins[i]);
            }
        }

Wobei ich die Skalierung immer noch vollkommen irreführend finde. Gerade in deinem letzten Beispiel sieht man das gut. Sowohl das erste Fach mit 20, als auch das 4. mit 8443 Kugeln, werden mit jeweils nur einer Einheit dargestellt. Wenn man von ganz weit weg schaut, sieht man zwar die entstehende Binomialverteilung, aber die Kurve stimmt vorne und hinten nicht in der Darstellung (was natürlich an der Konsole liegt als Ausgabemedium).

Baka wa shinanakya naoranai.

Mein XING Profil.

3.170 Beiträge seit 2006
vor 13 Jahren

Hallo talla,

Über den SInn und Unsinn der Skalierung kann man sicherlich streiten, vor allem hast Du recht mit Deinen Aussagen:

Gerade in deinem letzten Beispiel sieht man das gut. Sowohl das erste Fach mit 20, als auch das 4. mit 8443 Kugeln, werden mit jeweils nur einer Einheit dargestellt.

und

sieht man zwar die entstehende Binomialverteilung, aber die Kurve stimmt vorne und hinten nicht in der Darstellung

Und ich hnbe recht , wenn ich behaupte, daß die Kurven wenn sie skaliert sind vom optischen Eindruck her leichter untereinander zu vergleichen sind.

Die Aufgabe ist jedenfalls mit der erweiterten Ausgabefunktion gelöst und Du bist dran 🙂

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

6.862 Beiträge seit 2003
vor 13 Jahren

Okay,

ich hab ne kleine Implementierungsaufgabe bei der es mehr um Wissen geht, als irgend einen Standardalgorithmus zu implementieren.

Gegeben ist ein Stück Code names Add welcher eine Addition von 4 Werten übernehmen soll. Er soll dabei folgendermaßen aufgerufen werden können:


            var erg = Add(1, 2, 3, 4);
            var erg2 = Add.SomethingNice()(1)(2)(3)(4);

In beiden Variablen, erg und erg2 soll als Ergebnis die Summe der Argumente, also 10, stehen.

Nun die Frage: Wie müsste Add aussehen und wie SomethingNice? Wie nennt man das, was da passiert. Ist es vielleicht gar nicht möglich in C# sowas zu schreiben?

EDIT: Kleine Ergänzung (die im Prinzip auch ein, gar nicht mal so unbedeutender, Tipp ist):
Sowas soll auch möglich sein:


            var tmp = Add.SomethingNice()(1)(3);
            var erg3 = tmp(4)(2);

Ergebniss ist natürlich wieder die Summe der Argumente, also 10.

Baka wa shinanakya naoranai.

Mein XING Profil.

C
401 Beiträge seit 2007
vor 13 Jahren

Ich gehe zwar davon aus, dass du eigentlich eine Lösung in F# haben wolltest, da es aber ja auch Scala für die .Net Plattform gibt hab ich es mal in Scala gemacht:


object Main {

  /**
   * @param args the command line arguments
   */
  def main(args: Array[String]): Unit = {
    println(Add(1,2,3,4))
    val add5 = Add.somethingNice(2)(3) _
    println(add5(2)(3))
  }

}

object Add {
  def apply(a: Int, b: Int, c: Int, d: Int) = somethingNice(a)(b)(c)(d)
  def somethingNice(a: Int)(b: Int)(c: Int)(d: Int) = a + b + c + d
}

6.862 Beiträge seit 2003
vor 13 Jahren

Ich gehe zwar davon aus, dass du eigentlich eine Lösung in F# haben wolltest

Dann hätte ich was gesagt. Ich dachte die Frage danach, ob es in C# geht, deutet darauf hin, dass ich natürlich eine Lösung in C# möchte 😃 Daher kann ich deine Lösung so nicht gelten lassen (ich kann praktisch kein Scala, aber soweit ich deinen Code verstehe macht er schon prinzipiell das wonach ich gefragt habe, halt auf die Scala Art)

EDIT: Durch die Antwort ist ja praktisch schon verraten das es natürlich in C# geht - ich hätte neben der Lösung aber auch noch den Namen der Technik ( aber bitte beides zusammen, nur die Nennung des Namens würde die Lösung dann durch suchen im Inet trivial machen)

Baka wa shinanakya naoranai.

Mein XING Profil.