Quadrat- und Kubikwurzeln berechnen vor 30 Jahren und heute ;-)

Unsere Rechner können sehr viele Rechenoperationen schon auf der CPU erledigen, wenn es darum geht, mit den Standardtypen (z.b. double, dem gängigen 8-Byte-Fließkommaformat) zu rechnen. Das war gegen Anfang der 80er Jahre noch nicht so, da konnten typische CPUs nur gerade mit 8-Bit-Ganzzahlen addieren, subtrahieren und ein paar Bit-Operationen ausführen und doch ließ sich daraus alles aufbauen. Langzahladdition und -subtraktion waren trivial, Multiplikation und Division etwas schwieriger, aber noch gut machbar. Aber für die Quadratwurzeln musste man schon etwas genauer überlegen. Das mitgelieferte Basic konnte das für 5-Byte-Fließkommazahlen, aber das Verfahren war einfach nur dumm und langsam, den es wurde der Logarithmus genommen, halbiert und dann die Exponentialfuntion. Mathematisch völlig richtig, aber eben langsam und mit unnötigen Rundungsfehlern behaftet. Wer sich ein bisschen mit dem Thema auskennt, landet recht schnell bei dem Newton-Verfahren. Man schätzt die Quadratwurzel, z.B. von 2, dividiert 2 durch den Schätzwert und nimmt dann den Mittelwert von Schätzung und Quotient für die nächste Iteration. Zu offensichtlich, um es nicht so zu machen und in der Praxis auch ganz brauchbar. Es gibt aber ein Verfahren, wie man früher ähnlich wie die Division auch die Quadratwurzeln „handschriftlich“ berechnen konnte und dieses Verfahren lässt sich sehr gut verwenden, um eine Quadratwurzelberechnung zu schreiben. Hier ist eine Ruby-Implementierung, die von Ganzzahlen ausgeht:
Zu einer ganzen Zahl x \ge 0 werden ganze Zahlen r \ge 0 und s \ge 0 gesucht so dass x = r + s^2 und (s+1)^2 > x gilt.

  def check_is_int(x, name="x")
    raise TypeError, "#{name}=#{x.inspect} must be Integer" unless x.kind_of? Integer
  end

  #
  # helper method for internal use: checks if word_len is a reasonable
  # size for splitting a number into parts
  #
  def check_word_len(word_len, name="word_len")
    raise TypeError, "#{name} must be a positive number <= 1024" unless (word_len.kind_of? Fixnum) && word_len > 0 && word_len <= 1024
    word_len
  end

  #
  # split number (Integer) x into parts of word_len bits each such
  # that the concatenation of these parts as bit patterns is x
  # (the opposite of merge_from_words)
  #
  def split_to_words(x, word_len = 32)
    check_word_len(word_len)
    check_is_int(x, "x")
    m = x.abs
    s = (x <=> 0)
    bit_pattern = (1 << word_len) - 1
    words = []
    while (m != 0 || words.length == 0) do
      w = m & bit_pattern
      m = m >> word_len
      words.unshift(w)
    end
    if (s < 0) then
      words[0] = -words[0]
    end
    words
  end


  #
  # calculate the an integer s >= 0 and a remainder r >= 0 such that
  # x = s**2 + r and s**2 <= x < (s+1)**2
  # the wordwise algorithm is used, which works well for relatively
  # large values of x.  n defines the word size to be used for the
  # algorithm.  It is good to use half of the machine word, but the
  # algorithm would also work for other values.
  #
  def sqrtw_with_remainder(x, n = 16)
    check_is_int(x, "x")
    check_is_int(n, "n")

    n2 = n<<1
    n1 = n+1
    check_word_len(n2, "2*n")

    s = (x <=> 0)
    if (s == 0) then
      return [0, 0]
    elsif (s < 0)
      a = sqrtw_with_remainder(-x)
      return [ Complex(0, a[0]), a[1]]
    end

    xwords = split_to_words(x, n2)
    if (xwords.length == 1) then
      return sqrtb_with_remainder(xwords[0])
    end

    xi = (xwords[0] << n2) + xwords[1]
    a  = sqrtb_with_remainder(xi)
    yi = a[0]
    if (xwords.length <= 2) then
      return a
    end

    xi -= yi*yi
    2.upto(xwords.length-1) do |i|
      xi = (xi << n2) + xwords[i]
      d0 = (yi << n1)
      q  = (xi / d0).to_i
      q0 = q
      j  = 0
      was_negative = false
      while (true) do
        d = d0 + q
        r = xi - (q * d)
        break if (0 <= r && (r < d || was_negative))
        if (r < 0) then
          was_negative = true
          q = q-1
        else
          q = q+1
        end
        j += 1
        if (j > 10) then
          break
        end
      end
      xi = r
      yi = (yi << n) + q
    end
    return [ yi, xi ]
  end

  #
  # calculate an integer s >= 0 and a remainder r >= 0 such that
  # x = s**2 + r and s**2 <= x < (s+1)**2
  # the bitwise algorithm is used, which works well for relatively
  # small values of x.
  #
  def sqrtb_with_remainder(x)
    check_is_int(x, "x")

    s = (x <=> 0)
    if (s == 0) then
      return [0, 0]
    elsif (s < 0)
      a = sqrtb_with_remainder(-x)
      return [ Complex(0, a[0]), a[1]]
    end

    xwords = split_to_words(x, 2)
    xi = xwords[0] - 1
    yi = 1

    1.upto(xwords.length-1) do |i|
      xi = (xi << 2) + xwords[i]
      d0 = (yi << 2) + 1
      r  = xi - d0
      b  = 0
      if (r >= 0) then
        b  = 1
        xi = r
      end
      yi = (yi << 1) + b
    end
    return [yi, xi]
  end

Daraus lässt sich dann relativ einfach eine Quadratwurzelfunktion mit entsprechender Nachkommstellenberechnung bilden.

Witzerweise lässt sich das Verfahren auch für Kubikwurzeln sinngemäß anwenden.

Hier ein kleiner Test:

irb(main):138:0> sqrtw_with_remainder(2)
=> [1, 1]
irb(main):139:0> sqrtw_with_remainder(200)
=> [14, 4]
irb(main):140:0> sqrtw_with_remainder(20000)
=> [141, 119]
irb(main):141:0> sqrtw_with_remainder(2000000)
=> [1414, 604]
irb(main):142:0> sqrtw_with_remainder(200000000000000000000000000000000000000000000000000000000000)
=> [447213595499957939281834733746, 228299936041363866321288807484]
irb(main):143:0> sqrtw_with_remainder(2000000000000000000000000000000000000000000000000000000000000)
=> [1414213562373095048801688724209, 1974464361663955412145937324319]
irb(main):144:0> sqrtw_with_remainder(3000000000000000000000000000000000000000000000000000000000000)
=> [1732050807568877293527446341505, 3021967735564464902990914334975]
irb(main):145:0> sqrtw_with_remainder(3)
=> [1, 2]
irb(main):146:0> sqrtw_with_remainder(300)
=> [17, 11]
irb(main):147:0> sqrtw_with_remainder(30000)
=> [173, 71]
irb(main):148:0> sqrtw_with_remainder(3000000)
=> [1732, 176]
Share Button

Elixir Programming Language

Deutsch

When there is need for high performance, scalability and availability of applications by using the parallelism that current hardware can offer, Erlang is on the table. It uses its own virtual machine, called BEAM. So in this aspect it is quite similar to Java and to most modern interpreted languages. They all use their VM under the hood. Erlang is functional and uses its own processes, which can be better understood by thinking of actors rather than of OS-level processes or threads. Communication between these Erlang processes is done by messages.

Since the JVM has become very popular in the IT world due to its maturity, diffusion and the knowhow in running it, there has been demand for better JVM programming languages than Java, which has lead to developments like Scala, JRuby, Clojure, Groovy, Ceylon and a few dozen more.

Elixir now brings the same idea to the Erlang world. The language is still Alpha and we will see were it will go. But the basic idea is having a Ruby-like syntax, some concepts and ideas from Clojure and off course the possibilities and paradigms from the Erlang world combined. So again it is possible to run many Erlang- or Elixir-processes and let them communicate via a highly efficient messaging system.

It will be interesting how this will compare to Erlang itself and to JVM-based solutions like the Akka-framework with Scala in terms of performance and stability.

Links:

Share Button

Elixir

English

Wenn es darum geht, hochperformante, skalierbare und hochverfügbare Applikationen zu entwickeln, die die Parallelisierungsmöglichkeiten der heutigen Hardware gut ausnutzen, ist Erlang ein Klassiker. Dieses verwendet eine virtuelle Maschine (BEAM), ähnlich wie Java und wie die meisten modernen interpretierten Sprachen. Erlang ist funktional und verwendet eigene Prozesse, die man eher als Aktoren denn als Betriebssystemprozesse verstehen sollte, zwischen denen via Messages kommunziert wird.

So wie in der IT-Welt die Java-VM sehr geschätzt wird, und sei es auch nur wegen ihrer großen Verbreitung und des Knowhows im Betrieb, so hat doch das Bedürfnis nach bessere Programmiersprachen für die Java-VM zu Entwicklungen wie Scala, JRuby, Clojure, Groovy, Ceylon und Dutzenden, weiteren Sprachen für die Java-VM geführt.

Diese Idee bringt nun Elixir in die Erlang-Welt. Die Sprache ist noch im Alpha-Stadion und wir werden sehen, was sich daraus entwickelt. Die Idee ist grundsätzlich, dass man eine Syntax verwendet, die sich an Ruby anlehnt, einige Ideen aus Clojure übernimmt und natürlich dann die Möglichkeiten und Paradigmen der Erlang-Welt in diesem Rahmen anbietet. Man hat also auch wieder beliebig viele „Prozesse“, wiederum im Sinne des Aktoren-Patterns, nicht als Betriebssystemprozesse oder -threads und eine Kommunikation via Messages.

Es ist sicher interessant, zu beobachten, wie sich das entwickelt und wie brauchbar es in Bezug auf Performance und Stabilität im Vergleich mit Erlang, aber auch mit JVM-basierten Lösungen wie dem Akka-Framework mit Scala sein wird.

Links dazu

Share Button

Serialisierung

English

Serialisierung ermöglicht es, Objekte mehr oder weniger vollständig und verlustfrei zu speichern oder über das Netzwerk zu übertragen. So etwas konnte man schon immer machen, aber früher musste man die Objekte „von Hand“ serialisieren, also jeweils die Funktionalität schreiben, die das leistet. Auch wenn es damals noch nicht Objekte waren.

Java hatte dann plötzlich so eine Serialisierung für (fast) alle Objekte an Bord und man musste nur noch ObjectOutputStream und ObjectInputStream verwenden und konnte alle seine Objekte speichern und wieder zurücklesen. Sogar Referenzen, die mehrmals dasselbe Objekt referenzierten, wurden richtig behandelt. Neu war die Idee wohl nicht wirklich, andere Programmiersprachen konnten das schon viel früher, aber das hat kaum jemand gemerkt.

Dass dann doch eine gewisse Ernüchterung aufkam, soll hier nicht verschwiegen werden:

  • Die Markierung mit dem Serializiable-Interface ist konzeptionell eine schlechte Lösung, weil sie annimmt, dass abgeleitete Klassen automatisch auch serialisierbar sind, was nicht stimmt. Besser wäre ein Unserializable-Interface gewesen. Oder heute eine Annotation..
  • Mit dieser SerialVersionUID hat man eine Menge Ärger. Soll man sie immer nachführen, wenn sich das Interface ändert? Oder lieber den automatischen Mechanismen vertrauen? So oder so bleibt das Problem der inkompatiblen Versionen, das nicht wirklich gut gelöst und vielleicht auch nicht einfach lösbar ist.
  • Die Serialisierung bringt einen weiteren, verdeckten Konstruktor ins Spiel.
  • Die Serialisierung unterläuft private und protected, denn alles muss ja in den Stream und man kann es manuell decodieren. Allerdings kann man private sowieso mittels Reflection oder einfach durch Source-Code oder gar Byte-Code-Änderungen austricksen.
  • Die Objektidentität geht bei der Serialisierung verloren. Man kann so leicht verschiedene Kopien von einem Objekt erzeugen. Das muss man bewusst behandeln.
  • Manchmal muss man doch manuellen Serialisierungs-Code schreiben, etwa für Singletons. Das vergisst man bei dem „alles automatisch“-Ansatz leicht, aber wenn man die Serialisierung selber schreibt, vergisst man auch, dass die schöne Copy-Paste-Orgie an genau dieser Stelle einmal eine Pause machen muss
  • Die eingebaute Serialisierung ist langsam
  • Es ist recht mühsam, aber durchaus möglich, ein Objekt in eine Byte-Sequenz zu serialisieren.
  • Das Format ist binär und nicht sehr gut lesbar. Schöner wäre es, wenn man den Serialisierungs-Mechanismus wählen könnte, z.B. JSON, XML, verschiedene Binärformate…
  • Die Serialisierung verleitet dazu, Kommunikation über dieses Binärformat zu machen und damit eine sehr enge Kopplung zu erzwingen
  • Die Serialisierung verleitet dazu, sie für Persistenz zu benutzen und damit wohldurchdachte, ausgereifte und eventuell sogar transaktionale Datenbanken zu unterlaufen. Ganz schlimm: Second Level Cache in Hibernate…

Es hatte sicher manchmal Vorteile, diese Serialisierung zur Verfügung zu haben und für viele Zwecke eignet sie sich auch halbwegs gut. Aber man sollte das ganze doch je nach Anwendungsfall kritisch hinterfragen und gegebenenfalls andere Ansätze in Betracht ziehen oder zumindest den Schwächen Rechnung tragen. Generell wird die native Java-Serialisierung heute als einer der schwerwiegendsten Fehler in der Entwicklungsgeschichte von Java verstanden und man hat sie leider noch in anderen JVM-Sprachen geerbt. Es gibt heute viele Wege, „Serialisierung“ besser zu machen.

Für die serialVersionUID gibt es verschiedene Ansätze. Man kann eine statische Java-Methode schreiben, die so etwas wie „$Id$“ aus svn auswertet und daraus einen numerischen Wert gewinnt. So sind zwei verschiedene Dateien immer mit verschiedener serialVersionUID versehen, wenn sie durch das Sourcecode-Management-System gelaufen sind. Mit git ist es etwas schwieriger, aber man kann das auch umsetzen. Ich gehe hier nicht näher darauf ein, da man die Java-Serialisierung heute ohnehin skeptisch sieht.

Ansonsten ist es vernünftig, serialVersionUID normalerweise leer zu lassen, wenn man damit nicht gezielt arbeiten will und es sich entsprechend in die Checkliste aufnimmt. So ein Feld serialVersionUID zu pflegen ist eigentlich krank und sollte nur im Ausnahmefall mit guten Gründen und einem guten Konzept gemacht werden. Wer trotzdem noch mag oder von den Projektvorgaben gezwungen wird, kann serialVersionUID zufällig erzeugen, z.B. mit dem folgenden Perl-Script:

#!/usr/bin/perl
use bigint;
use Math::Random::Secure qw(irand);
my $r = (irand() << 32) + irand();
printf "%20d\n", $r;

Das ist immer noch etwas besser, als wenn der einmal von der IDE generierte Wert oder die 1 oder 0, mit der man angefangen hat, ewig gleich dort stehen bleibt. Aber man muss den Wert jedes Mal ändern, wenn es größere Änderungen gibt, die das Serialisierungsformat beeinflussen.

Share Button

Eigene Collection-Klassen

Wer braucht eigene Collection-Klassen? Java, Perl, Ruby, Scala, Clojure, sie alle haben gute Bibliotheken und da sind sehr schöne Collection-Klassen verfügbar und wenn die mitgelieferten nicht reichen, findet man noch passendere. Es lohnt sich zu suchen.
Gelegentlich braucht man komplexere Collections, z.B. Mengen, die noch eine Gruppierung in disjunkte Teilmengen aufweisen und wahlweise über die einzelnen Teilmengen oder als Gesamtmenge angesprochen werden können. Man denke an die Menge aller Räume in einem Gebäude und die Teilmengen der Räume in einem bestimmten Stockwerk. So etwas läßt sich leicht aus den vorhandenen Strukturen zusammensetzen, man muss nur noch schauen, dass man das entsprechende Interface für die Gesamtmenge implementiert.

Es gibt aber durchaus auch Fälle, wo man aus Performance-Gründen wirklich dezidierte Collections für den Anwendungsfall braucht und weder im Lieferumfang noch im Netz in der erfordelichen Qualität findet. Da typische Collection-Klassen sehr allgemein und flexibel gehalten sind, kann man Annahmen treffen und nutzen und so einiges abkürzen. Nur als Beispiel ließ sich einmal in Java eine Map durch eine eigene Implementierung ersetzen, die für die Schlüssel vom Typ long optimiert war und wesentlich weniger Speicher verbrauchte, solange die gespeicherten Wert-Objekte relativ klein waren. Da dies eine große Applikation war, die auf Servern wirklich das Memory aufgebraucht hat und man noch in der Begrenzung der 32-Bit-Welt agieren musste, ließ sich dadurch das Problem des zu großen Speicherverbrauchs in den Griff bekommen. Voraussetzung war natürlich, dass diese Tabellen einen großen Teil des Speichers belegten.

Share Button

WLAN-Router

Aus Sicherheits- und Durchsatzgründen ist es immer noch eine gute Idee, ein Netzwerk mit Kabeln anzulegen. Da wir so arbeiten, dass das Home-Verzeichnis auf einem NFS-Server liegt und alle Dateien auf diesem Rechner gespeichert werden und außerdem X11-Applikationen gelegentlich auf anderen Rechnern mit umgeleitetem Display laufen, ist ein zuverlässiges und schnelles und sicheres lokales Netzwerk sehr wichtig. Die Verkabelung im Gebäude ist dafür eingebaut worden.

Aber ein WLAN-Netzwerk ist trotzdem sinnvoll. Inzwischen können fast alle Mobiltelefone damit umgehen und man kann z.B. mit Signal oder Threema oder Skype telefonieren und das auch Gästen zur Verfügung stellen. Da Mobiltelefone und andere Mobilgeräte, die WLAN können, sowieso an anderen Orten direkt und ohne Firewall im Internet sind, habe ich eine Firewall zwischen WLAN und dem Kabelnetz eingefügt. Witzigerweise bieten die üblichen WLAN-Router zwar eine rudimentäre Firewall-Funktionalität, aber nur gegenüber der Netzwerkverbindung nach außen, weil anscheinend kaum jemand das Kabelnetz und as WLAN trennen will. Das lässt sich alles lösen, weil man natürlich die Firmware des WLAN-Routers austauschen kann und wenn nicht einen kaufen kann, bei dem das geht. Letztlich erwies es sich aber als einfacher, eine dezidierte Firewall zu kaufen und dazwischen zu schalten.

Für das WLAN selbst ist nun die Verschlüsselung eigentlich nicht sonderlich wichtig, sie erschwert es nur Gästen, das Netz zu benutzen. Da ja alle Geräte, die das WLAN benutzen, auch gelegentlich öffentliche WLAN- oder Mobilnetze verwenden, ist es sowieso notwending, die Geräte genügend abzusichern oder deren normaler Konfiguration zu vertrauen.

Hierzu kann man auch bei Bruce Schneier lesen, warum er sein
WLAN offen hat.

Share Button

Akka-Framework

Die Idee ein Framework zu entwickeln, dass ganz auf Messages zur Kommunikation zwischen den Komponenten basiert, ist interessant. Ich habe schon verschiedene Ansätze in der Java-Welt gesehen, z.B. Versuche, das in JavaEE mit JMS zu bauen. Letztlich ist das in der Java- und JVM-Welt ein eher selten verwendeter Ansatz, aber z.B. Erlang-Software basiert komplett auf diesem Prinzip, wobei natürlich effiziente Messaging-Mechanismen vorausgesetzt werden müssen.

Das Akka-Toolkit oder Akka Framework setzt diese Idee nun in konsequenter Weise für die Java- und JVM-Welt um, wobei es natürlich am besten in seiner nativen Sprache, also in Scala verwendet wird. Die Idee ist, eine effiziente massive Parallelisierung hinzubekommen und durch assynchrone Kommunikation die Komponenten weitestgehend zu entkoppeln. Typisch in der Scala-Welt ist es, dass man unveränderliche (deep immutable) Objekte in den Messages verschickt, um Änderungskonflikte und Fehler zu vermeiden, wenn dasselbe Objekt an mehreren Orten gleichzeitig verwendet wird. Veränderbare Objekte in den Messages sollte man vermeiden, außer man kennt das Framework und die Multithreading-Thematik sehr gut und weiss was man tut und es ist aus Performance-Gründen wirklich ein so großer Vorteil, dass der zusätzliche Entwicklungs- und Testaufwand gerechtfertigt ist. Dabei ist noch zu sagen, dass sich solches Verhalten kaum testen lässt, weil das Zeitverhalten in den Tests nie genau das im ungünstigsten Fall auf dem Produktivsystem sporadisch auftretende Zeitverhalten ist. So können in schlecht oder fahrlässig programmierten Applikationen noch Fehler lauern, die erst irgendwann auf dem Produktivsystem auftreten und die sich dann noch schlecht einordnen lassen. Genau deshalb sollte man hier auf Nummer sicher gehen und nur deep-immutable Objekte in Messages verschicken.

Für die Parallelisierung von Software gibt es grundsätzlich verschiedenen Ansätze. Mehrere getrennte Prozesse sind attraktiv, weil die Trennung sauber ist und man nur bei der Kommunikation überhaupt aufpassen muss. Dafür sind sie etwas schwergewichtiger als Threads und vor allem ist der Aufwand für de Kommunikation größer. Mehrere Threads zu verwenden hat den Vorteil, dass man dasselbe Memory benutzt und deshalb Daten einfacher getauscht werden können. Dafür steigen auch die Risiken, wenn mehrere Zugriffe auf dasselbe Objekt gleichzeitig stattfinden und mindestens einer davon schreibend ist.

Akka verwendet sogenannte Aktoren (engl. Actors), die einen Memory-Footprint von unter 1 k haben sollen, weshalb man etwa eine Million Actors durchaus gleichzeitig im Memory haben kann. Nun werden diesen Actors Messages geschickt, die etwa Methodenaufrufen in der (synchronen) objektorientierten Denkweise entsprechen, aber assynchron sind. Das heißt, dass der Sender die Message verschickt und dann fertig ist. Sie wird in einer Warteschlange für den passenden Actor eingereih und dann irgendwann abgearbietet. Nun hat so ein Actor nicht permanent einen Thread zugewiesen, sondern es gibt einen Threadpool. Actors mit einer nicht-leeren Warteschlange werden dann einem Thread im Threadpool zugewiesen und so abgearbeitet.

Share Button

Telearbeit

Durch die heutigen Möglichkeiten wie VPN, Mobiltelefonie, Videotelefonie, Chat, Telefon, EMail, Zugriff auf Fileserver u.s.w. kann man neuerdings von zuhause aus arbeiten. Wenn man die Sache genauer anschaut, ist natürlich das bezahlbare Festnetztelefon, das es schon eine Weile gibt, schon für viele Tätigkeiten schon völlig ausreichend und auch das Internet gab es schon vor 20 Jahren, als das alles noch einfacher ging, weil noch kaum jemand an Firewalls dachte, die einem das Leben für den Zugriff von außen erschweren könnten. Aber natürlich hat man heute noch mehr Möglichkeiten und hat auch 20 Jahre Zeit gehabt, darüber nachzudenken. Attraktiv ist es ja, weil man dadurch Wege spart und die Umwelt schont. Manche Firmen verzichten sogar auf einen festen individuellen Büroarbeitsplatz und lassen die Mitarbeiter 20% der Zeit von zuhause arbeiten, um Kosten zu sparen. Gerade Informatikberufe eignen sich dafür.

In der Praxis zeigt es sich, dass es oft sinnvoll ist, jeweils an dem Ort zu arbeiten, der für die aktuelle Tätigkeit am effizientesten ist. Das ist meistens der Ort, wo die Leute sind, mit denen man zusammenarbeitet, weil einfach die direkte Kommunikation immer noch viel besser funktioniert als über elektronische Medien. Es kann aber auch gerade ein Ort sein, der leiser ist als das Großraumbüro, in dem alle dauernd telefonieren oder Besprechungen am Arbeitsplatz durchführen. Es gibt auch recht viele Firmen, bei denen alle oder fast alle Mitarbeiter dauernd von zuhause aus arbeiten, wenn sie nicht gerade für die Firma unterwegs sind, z.B. für das alljährliche Mitarbeitertreffen. Solche Firmen sind dann über viele Länder verteilt und haben in jedem dieser Länder einen oder mehrere Mitarbeiter. Man sieht das häufig bei Open-Source-Firmen und mir sind viele Beispiele dafür bekannt.

Nun ist das verlockend, man kann sich die Wege sparen und sich sein Büro wirklich individuell einrichten. Aber es hat auch Nachteile. Solange die Arbeitswege nicht zu lang sind, ist es auch ein Luxus, nach Hause zu kommen und nicht mehr arbeiten zu müssen. Diese Grenze verschwimmt leicht einmal, wenn man oft zuhause arbeitet. Lohnt sich das? Die Frage kann man wohl nur individuell beantworten. Die zweite Frage, die ja niemand zu stellen wagt, ist ob denn da wirklich so intensiv gearbeitet wird. Es gibt Tätigkeiten, die von sich aus schon einen Rhythmus vorgeben oder bei denen das Ergebnis leicht messbar ist. Es gibt aber auch Tätigkeiten, die sich nicht so leicht fassen lassen. Da funktioniert diese Telearbeit gut, wenn sich die Mitarbeiter dem gemeinsamen Ziel verpflichtet fühlen und motiviert sind, ihren Teil dazu beizutragen. Man kann das durchaus lernen.

Wichtig ist an solchen Tagen, dass man auch innerhalb der eigenen Wohnung klare Abgrenzungen zwischen Arbeitszeit und Freizeit findet, auch um die Freizeit zu schützen. Ein fester Tagesablauf hilft dabei sicher und vielleicht ist es sogar gut, wenn man zwei verschiedene Rechner für die Arbeit und die Freizeit hat. Das haben in dem Fall die meisten, weil es einen Firmenlaptop und einen privaten Rechner gibt, die nur selten in einem Gerät vereinbar sind.

Hierzu ein paar andere Blogbeiträge und Artikel auf Englisch:

Share Button

iO hat zusätzliche Services

iO der Swisscom bietet jetzt die Möglichkeit, abhängig vom zugrundeliegenden Abo zusätzliche Möglichkeiten für jeweils einen Monat dazuzukaufen. Damit kann man aus der iO-App Nummern in der Schweiz oder mit dem passenden zugrundeliegenden Abo sogar Nummern aus bestimmten Ländenr in Europa und Nordamerika. Für diejenigen, die im Urlaub WLAN auf dem Zeltplatz oder Hotel zur Verfügung haben, kann das nützlich sein und Roaminggebühren sparen.

Update 2019-03-23: https://io.swisscom.ch/ ist nicht mehr erreichbar. iO gibt es auch seit über einem Jahr nicht mehr.

Share Button

Kovarianz und Kontravarianz

Bei Typsystemen objektorienter Programmiersprachen wird man gelegentlich mit Kovarianzu und Kontravarianz konfrontiert. Im Fall von Java stellt man sogar fest, dass bei Arrays hier ein konzepitioneller Fehler unterlaufen ist, den man heute nicht mehr wegbekommt.

Wenn man zum Beispiel die Vererbungshierarchie

Frucht -> Citrusfrucht -> Zitrone

hat, dann ist es intuitiv plausibel, anzunehmen, dass eine Liste von Citrusfrüchen sowieso immer auch eine Liste von Früchten ist. Das ist das Prinzip der Kovarianz. Wenn man mal in der Java-Welt das anschaut, dann kann eine Methode, die aus einer Liste Objekte entimmt und damit irgendwelche Berechnungen macht, durchaus gut damit leben, wenn statt der erwarteten List eine List kommt, denn was daraus gelesen wird, sind ja alles gleichzeitig auch Citrusfrüchte. Wenn diese Liste nun noch immutable ist, stimmt die Sache sogar. Dagegen würde eine List nicht funktionieren.

Nun wird es aber gefährlich, wenn die Methode dort Objekte hinzufügt. Das ist von den Sprachkonstrukten her durchaus möglich. Wenn aber nun Liste erwartet wird und stattdessen List übergeben wird und dort Orangen hinzugefügt werden, so ist das gegen die Idee einer Liste von Zitronen. Dagegen könnte man die Orangen sehr gut in eine List einfügen. Hier kommt das Prinzip der Kontravarianz zum Zuge.

Bei den Generics wurden Kovarianz und Kontravarianz einigermaßen richtig berücksichtigt, bei Arrays wurde aber nur die Kovarianz wahrgenommen, was wie erwähnt falsch ist.

In Scala sind diese Konzepte viel präziser und sauberer umgesetzt.

Siehe auch: Wikipedia

Share Button