Anzahl der Ziffern einer Zahl ermitteln 👍 👎

Oftmals sehe ich zur Ermittlung der Anzahl der Ziffern einer Zahl, dass diese explizit in eine Zeichenkette konvertiert und anschließend mit Zeichenketten-Operationen bearbeitet wird. Unter sehr dynamischen Sprachen wie beispielsweise PHP wird einem dies besonders einfach gemacht.

Das funktioniert zwar zweifelsohne und kann je nach Kontext auch der schnellste Weg sein, mathematisch elegant ist das aber nicht und darum möchte ich zumindest der Vollständigkeit wegen gerne aufzeigen, wie sich dieses Problem rein rechnerisch lösen lässt.

Dazu erinnern wir uns zuerst einmal an den Begriff der Größenordnung, welche eine Zahl zu gegebener Basis (im Alltag also meistens die 10, bei Informatikern evtl. auch noch 2, 8 oder 16 Smiley: winking) um eben diesen Faktor erhöht. Anschaulicher lässt sich dies als Potenz vorstellen, bei der der Exponent inkrementiert wird. Da dabei praktisch im jeweiligen Stellenwertsystem auch die Anzahl der Ziffern der jeweiligen Zahl steigt, fällt dem geneigten Leser möglicherweise bereits ein Zusammenhang auf. Zuerst einmal wollen wir die bisherigen Kenntnisse jedoch der Übersicht wegen formal notieren:
Größenordnungen
0102030405060708091011
   1 = 100    // eine Stelle   5 = 100,69  // eine Stelle  10 = 101    // zwei Stellen  15 = 101,17  // zwei Stellen 100 = 102    // drei Stellen 999 = 102,99  // drei Stellen1000 = 103    // vier Stellen1234 = 103,09  // vier Stellen
Es besteht offensichtlich ein Zusammenhang zum Exponenten der Basis des Zahlensystems, wie groß diese Zahl ist – was ja auch völlig klar so sein muss und letztlich keine besondere Erkenntnis ist. Genau diese Rückbesinnung auf Bekanntes bringt uns jedoch zur Lösung des Problems: Wir müssen zu unserer gegebenen Zahl den Exponenten zur Basis des gegebenen Zahlensystems suchen, wodurch unsere Zahl erzeugt wird.

Mit gewissem Entsetzen denken nun vielleicht die ersten Leser spontan an Wurzeln (und nebenbei an ihre letzte Wurzelbehandlung Smiley: grinning), aber es ist "noch schlimmer": Da uns Wurzeln die Basis liefern, wir jedoch den Exponenten benötigen und das Kommutativgesetz bei Potenzen im Allgemeinen nicht gilt (d. h. ab ≠ ba), benötigen wir die zweite Umkehroperation – den Logarithmus.

Dieser löst uns die Gleichung 10x = Zahl nach x auf und damit haben wir bereits beinahe die Lösung unserer eigentlichen Fragestellung. Da der Exponent auch Dezimalstellen enthalten kann, die Anzahl der Ziffern jedoch in jedem Fall eine natürliche Zahl sein muss, runden wir das Ergebnis erst einmal pauschal ab. Da die Zählung des Exponenten nun auch noch bei 0 beginnt (was uns als Softwareentwickler aber nur mäßig schockieren dürfte Smiley: cool), korrigieren wir diesen Versatz durch Addition von 1 und erhalten folgende Lösung:

Anzahl der Ziffern einer Zahl (im Dezimalsystem) = ⌊log10 Zahl⌋ + 1

Natürlich lässt sich dies auch in Programmen sehr einfach verwenden, daher wollen wir eine Erweiterungsmethode für ganze Zahlen in C# schreiben, die uns die Anzahl der Ziffern liefert:
Anzahl der Ziffern einer Zahl (Implementierung in C#)
010203040506070809
public static int GetDigitCount(this int number) {    if(number != 0) {        double baseExp = Math.Log10(Math.Abs(number));
return Convert.ToInt32(Math.Floor(baseExp) + 1); } else { return 1; }}
Wir berücksichtigen dabei die 0 separat und beugen außerdem Problemen mit negativen Zahlen durch Verwendung der Betragsfunktion vor. Die Anwendung gestaltet sich nun denkbar einfach:
Anzahl der Ziffern einer Zahl (Verwendung der C#-Implementierung)
01020304
int digitCount = (1).GetDigitCount();     // 1int digitCount = (12).GetDigitCount();    // 2int digitCount = (123).GetDigitCount();   // 3int digitCount = (1234).GetDigitCount();  // 4
Natürlich wäre auch die Implementierung in Form einer Schleife, welche die Zahl fortlaufend durch die Basis (10) teilt, denkbar, um die Anzahl der Ziffern zu ermitteln. Aber das sei dem geneigten Leser zur Übung überlassen. Smiley: tongue_out

Implizite Typisierung in C# 👍 👎

C# ist und bleibt zwar eine grundsätzlich statisch typisierte Programmiersprache, erlaubt jedoch auch die implizite Typisierung mit Hilfe des var-Schlüsselwortes:
Verwendung
01
var age = 24;  // äquivalent zu "int age = 24"
Der konkrete Typ wird dabei implizit durch den Wert auf der rechten Seite bestimmt. Dies kann den Quelltext besonders bei komplexen Strukturen (z. B. im Hinblick auf generische Programmierung) etwas kompakter gestalten, je nach Gewohnheit aber auch etwas schwerer zu verstehen sein. Es bleibt wie so oft dem Entwickler (bzw. den Richtlinien des Teams) überlassen, wie damit umzugehen ist.

Wichtig zu verstehen ist in jedem Fall, dass var in diesem Fall nur ein syntaktisches Hilfsmittel ist und keine dynamische Typisierung eingeführt wird (dazu in einem späteren Artikel mehr). Das bedeutet insbesondere, dass unserem "age" später nicht etwa eine Zeichenkette zugewiesen werden kann.

Zwingend notwendig ist diese Vorgehensweise jedoch bei anonymen Typen, da der Typ keinen (bzw. lediglich einen internen, dem Compiler bekannten) Typenbezeichner besitzt:
Verwendung bei anonymen Typen
0102030405
var ich = new {    FirstName = "Holger",    LastName = "Stehle",    Age = 24};
Auch im Rahmen von LINQ-Abfrageausdrücken kann dieses Vorgehen beispielsweise bei der Projektion von Daten aus einer Liste mittels Select(…) notwendig sein (aus ähnlichen Gründen wie im Beispiel zuvor).

Optionale und benannte Parameter in C# 👍 👎

Wie viele andere Programmiersprachen (z. B. PHP) unterstützt auch C# seit einiger Zeit optionale Parameter. Die Verwendung ist denkbar einfach:
Optionale Parameter
01020304050607080910
  // Kreisfläche berechnenpublic double GetCircleArea(double radius = 1) {    return (Math.PI * (radius * radius));}

double circleArea = GetCircleArea(5); // Kreisfläche für Radius "5"
double circleArea = GetCircleArea(1); // Kreisfläche für Radius "1"double circleArea = GetCircleArea(); // Kreisfläche für Radius "1"
Der beim letzten Aufruf weggelassene Parameterwert wird dabei implizit durch die Zuweisung des Standardwertes der Definition (radius = 1) gesetzt, um den Flächeninhalt des Einheitskreises zu erhalten. Aus naheliegenden Gründen sind optionale Parameter hinter nicht-optionalen Parametern zu definieren. Ein ähnliches Verhalten lässt sich selbstverständlich auch durch das Überladen der Methode erreichen, was jedoch etwas aufwändiger ist und zumindest in unserem Beispiel nicht ganz so intuitiv erscheint:
Optionale Parameter (per Methodenüberladung)
0102030405060708
  // Kreisfläche berechnenpublic double GetCircleArea(double radius) {    return (Math.PI * (radius * radius));}
public double GetCircleArea() { return this.GetCircleArea(1);}
Der Aufruf kann nun jeweils analog zu unserem vorherigen Beispiel erfolgen.

Darüber hinaus unterstützt C# jedoch auch benannte Parameter. Damit ist es nicht notwendig, die korrekte Position eines Parameters zu verwenden; diese werden stattdessen über ihren Namen angesprochen, was insbesondere im Zusammenhang mit (mehreren) optionalen Parametern sinnvoll sein kann:
Benannte Parameter
0102030405060708091011
  // Flächeninhalt berechnenpublic double GetArea(double height = 1, double width = 1) {    return (height * width);}

double area = GetArea(); // Höhe: 1, Breite: 1double area = GetArea(2); // Höhe: 2, Breite: 1double area = GetArea(3,4); // Höhe: 3, Breite: 4
double area = GetArea(width: 5); // Höhe: 1, Breite: 5 (benannter Parameter)
Es genügt dazu also, ganz einfach den Namen des Parameters, gefolgt von einem ":" (Doppelpunkt) und dem Wert anzugeben. Zu beachten gilt, dass benannte Parameter zwar auf per Position festgelegte Parameter folgen können, der umgekehrte Fall jedoch nicht unterstützt wird.

Index-basierter Zugriff auf Objekte in C# (und PHP) 👍 👎

Manchmal kann es sinnvoll (oder zumindest bequem oder intuitiv) sein, auf ein Objekt im Stile eines Feldes zugreifen zu können. Für das folgende Beispiel zu diesem Thema möchte ich kurz einen Anwendungsfall aus meiner Arbeit beschreiben.

Als ISP müssen wir bei Domains des Öfteren mit sog. "Handles" umgehen. Grob umrissen handelt es sich dabei um einen Kontakt-Datensatz für eine Domain. Darin abgelegt werden beispielsweise der Name, die Anschrift und – je nach TLD – auch noch div. andere Daten.

Im einfachsten Fall verwendet man zur Ablage dieser Informationen also ein assoziatives Feld (in C# z. B. ein generisches Dictionary) als Eigenschaft des Objekts:
"Handle"-Klasse
010203040506
public class Handle {    public Dictionary<string,string> HandleFieldValueList  {        get;        set;    }}
Den Umgang mit derartigen Datenfeldern habe ich zwar schon einmal relativ ausführlich beschrieben, dennoch möchten wir auch hier erst einmal das Objekt mit Daten füllen:
Daten hinzufügen
010203040506070809
Handle ich = new Handle() {      // Feldwerte    HandleFieldValueList = new Dictionary<string,string>() {        {"firstName", "Holger"},        {"lastName", "Stehle"}    }};
// ich.HandleFieldValueList["firstName"] enthält "Holger" etc.
Wie bereits angedeutet, können wir nun wie erwartet auf die entsprechenden Daten zugreifen. In einem solchen Fall – in dem die einzelnen Werte schließlich das Handle an sich ausmachen – wäre es doch aber auch praktisch, diesen Index-basierten Zugriff direkt auf dem Objekt durchführen zu können. Und genau dies ist mit C# ganz einfach möglich. Dazu müssen wir die Klasse wie folgt erweitern:
Indexer ergänzen
01020304050607080910111213141516171819
public class Handle {      // Indexer (-> Feldwerte)    public string this[string fieldName] {        get {            return this.HandleFieldValueList[fieldName];        }        set {            this.HandleFieldValueList[fieldName] = value;        }    }
// Feldwerte public Dictionary<string,string> HandleFieldValueList { get; set; }}
// ich["firstName"] enthält "Holger" etc.
Dies veranschaulicht die Implementierung eines sog. Indexers, welcher sich grundlegend wie eine Eigenschaft verhält. Der hauptsächlich interessante Teil string this[string fieldName] besagt, dass ein Index-basierter Zugriff auf die Instanz (→ this) mit einem Index (→ fieldName) des Typs string einen string liefert und definiert im Stile eines Akzessors, wie genau dies geschieht. Und schon können wir (wie im Code angedeutet) wie erhofft darauf zugreifen.

Indexer können auch überladen werden, so dass z. B. zusätzlich auch ein Zugriff mit numerischem Index ermöglicht werden kann. Zuletzt gilt zu beachten: Es handelt sich hierbei lediglich um eine etwas kompaktere Schreibweise für etwas, was sowieso möglich wäre. Dennoch gibt es – wie ich persönlich finde – interessante Einsatzmöglichkeiten hierfür. Der konkrete Nutzen ist also im Einzelfall abzuwägen.

Um derartiges Verhalten in PHP zu ermöglichen, muss die Klasse die Schnittstelle ArrayAccess implementieren. Ein Beispiel zur Umsetzung findet sich z. B. in der offiziellen Dokumentation.

Partielle Klassen in C# 👍 👎

C# unterstützt sogenannte partielle Klassen, welche es ermöglichen, eine Klasse auf verschiedene Dateien verteilt zu definieren. Diese werden bei der Kompilierung zu einer einzelnen Klasse zusammengeführt, so als wäre sie wie eine "normale" Klasse in einer Datei definiert worden. Insbesondere ist also auch dateiübergreifend Zugriff auf private Mitglieder (Eigenschaften, Methoden etc.) einer Klasse möglich.

Wir wollen dies wie üblich an einem (kleinen) Beispiel konkretisieren und legen dazu einfach die Eigenschaften und Methoden einer Klasse in zwei verschiedenen Dateien ab:
Klasse "Person" in Datei "Person.cs"
01020304050607080910111213141516
public partial class Person {    public Person(string firstName, string lastName) {        this.FirstName = firstName;        this.LastName = lastName;    }
// Partner liefern public Person GetPartner() { // z. B. aus Datenbank auslesen }
// Kinder liefern public List<Person> GetChildren() { // z. B. aus Datenbank auslesen }}
Klasse "Person" in Datei "Person.Properties.cs"
01020304050607080910111213141516
public partial class Person {    public string FirstName {        get;        set;    }
public string LastName { get; set; }
public int Age { get; set; }}
Zugegeben: Es ist fraglich, ob dieses Vorgehen – insbesondere in diesem trivialen Beispiel – überhaupt sinnvoll ist. Andererseits ergibt sich für den Aufrufer/Verwender der Klasse keinerlei Unterschied zur Definition in einer Datei, da letztlich eine einzige Klasse herauskommt. Insofern ist dem Entwickler (bzw. den Richtlinien des Teams) überlassen, ob und ggf. wie man damit umgehen möchte. Die Verwendung erfolgt jedenfalls ganz wie gewohnt:
Anwendung der partiell definierten Klasse
010203
Person ich = new Person("Holger", "Stehle") {    Age = 24};
Ein großer Vorteil ergibt sich jedoch bei der Verwendung von Code-Generatoren. Der generierte Code definiert dabei partielle Klassen. Diese können nun in einer eigenen Datei erweitert werden, ohne Gefahr zu laufen, bei einer neuen automatischen Codegenerierung alle Anpassungen zu verlieren. Visual Studio selbst setzt auf derartiges Vorgehen z. B. im Zusammenhang mit ADO.NET Entity Framework oder grafischen Benutzeroberflächen (vgl. "*.Designer.cs"-Dateien).

Andere Einsatzmöglichkeiten wären die übersichtliche Arbeit verschiedener Entwickler an einer umfangreicheren Klasse oder das Anbieten einer Anwendung mit verschiedenen Ausprägungen der Funktionalität einer Klasse, indem nur spezifische Dateien der partiellen Klasse kompiliert werden.

Mit der praktischen kleinen Erweiterung NestIn für Visual Studio (nicht Express) könnt ihr im Übrigen mit wenigen Mausklicks auch dafür sorgen, dass die Dateien wie aus der Entwicklungsumgebung bekannt verschachtelt dargestellt werden, was meiner Ansicht nach deutlich zur Übersicht beiträgt.

Projektverweise

Kategorien / Archiv  |  Übersicht RSS-Feed

Schlagworte

Suche