toaste: Quersumme + Rekursion = undefined

Hallo Zusammen

Wieso gibt folgendes Script als return value "undefined"???
Ich möchte gerne solange die Quersumme berechnen bis die Zahl nur noch eine Stelle hat.
Wenn ich die Variable temp vor dem return ausgebe ist alles ok, jedoch nach dem return ist sie "undefined". eigentlich sollte Sie 9 sein.

alert(QSum(389133));

function QSum(numb)
{
   var qsum = 0;
   numb += "";                               // to string
   var digits = numb.split('');              // split numbers
   for ( var i = 0; i < digits.length; i++ ) // add together
      qsum += parseInt(digits[i]);

var temp = qsum.toString();               // to string
   if (temp.length > 1)                      // do it again
   {
      QSum(qsum);
   }
   else
   {
//    alert(temp)                      // variable OK!
      return temp;                     // ?????????
   }
}

  1. Hello,

    if (temp.length > 1)                      // do it again
       {
          QSum(qsum);
       }
       else
       {
    //    alert(temp)                      // variable OK!
          return temp;                     // ?????????

    und wo wandert da im ersten Fall das Ergebnis hin?

    MfG
    Rouven

    --
    -------------------
    When the only tool you've got is a hammer, all problems start to look like nails.
  2. Moin!

    Ich möchte gerne solange die Quersumme berechnen bis die Zahl nur noch eine Stelle hat.

    Man kann Probleme ja auch dadurch komplizieren, dass man sie möglichst komplex erledigt.

    Die Quersumme rekursiv zu berechnen ist jedenfalls von allen denkbaren Gesichtspunkten aus höchst ineffektiv.

    Wenn das denn unumgänglich ist, sollte deine Rekursion so funktionieren:
    Ermittle die Quersumme der Sub-Zahl und addiere die aktuelle Zahl. Gib das Ergebnis zurück.

    Viel einfacher ist es logischerweise, den ohnehin schon gesplitteten String einfach zu iterieren und summieren.

    - Sven Rautenberg

    --
    "Love your nation - respect the others."
    1. Hi,

      Die Quersumme rekursiv zu berechnen ist jedenfalls von allen denkbaren Gesichtspunkten aus höchst ineffektiv.

      nicht von allen. Zur Übung rekursiver Programmierung beispielsweise dürften die meisten anderen Ansätze eher unbrauchbar sein ;-)

      Cheatah

      --
      X-Self-Code: sh:( fo:} ch:~ rl:° br:> n4:& ie:% mo:) va:) de:] zu:) fl:{ ss:) ls:~ js:|
      X-Self-Code-Url: http://emmanuel.dammerer.at/selfcode.html
      X-Will-Answer-Email: No
      X-Please-Search-Archive-First: Absolutely Yes
    2. Hallo Sven,

      Man kann Probleme ja auch dadurch komplizieren, dass man sie möglichst komplex erledigt.
      Die Quersumme rekursiv zu berechnen ist jedenfalls von allen denkbaren Gesichtspunkten aus höchst ineffektiv.

      wenn es um die gewöhnliche einfache Quersumme geht, hast du natürlich Recht. Offensichtlich meint der OP aber die sogenannte einstellige Quersumme. Die wird tatsächlich gebildet, indem man von einer Zahl zunächst die gewöhnliche Quersumme bildet, und solange das Ergebnis mehrstellig ist, von diesem wiederum die Quersumme errechnet, bis man ein einstelliges Ergebnis bekommt. Prädestiniert für eine Rekursion ...

      Wenn das denn unumgänglich ist, sollte deine Rekursion so funktionieren:
      Ermittle die Quersumme der Sub-Zahl und addiere die aktuelle Zahl. Gib das Ergebnis zurück.

      Das ist etwas anderes als der OP meint.

      Aber das eigentliche Problem des OP hat Rouven schon ganz richtig erkannt.

      So long,
       Martin

      --
      Niemand ist überflüssig: Er kann immer noch als schlechtes Beispiel dienen.
  3. Ich möchte gerne solange die Quersumme berechnen bis die Zahl nur noch eine Stelle hat.

    Das hatten wir hier letztes Jahr schon, dabei kam das raus
    http://javascript.jstruebig.de/javascript/55/

    Struppi.

  4. Hallo toaste,

    Zahlen mit String-Operationen zu bearbeiten ist recht unelegant. Theoretisch ist es auch langsamer (schließlich muss die Zahl erst in einen String gewandelt und der gesplittet werden), bei Scriptsprachen weiß man das natürlich nie so genau und wenn man das nur ein paar mal aufruft, merkt man es sowieso nicht.
    Außerdem macht Rekursion das in dem Fall wirklich eher umständlicher.
    Ich würde es also so versuchen (b ist die Basis zu der die Quersumme gebildet wird):

      
    function quersumme(x, b) {  
      var sum = 0;  
      while (x >= b) {  
        while (x > 0) {  
          sum += x % b;  
          x = Math.floor(x / b);  
        }  
        x = sum;  
      }  
      return sum;  
    }  
    
    

    Grüße

    Daniel

    1. Zahlen mit String-Operationen zu bearbeiten ist recht unelegant. Theoretisch ist es auch langsamer (schließlich muss die Zahl erst in einen String gewandelt und der gesplittet werden)

      Naja.

      while (x >= b) {
          while (x > 0) {

      du hast zwei Schleifen

      sum += x % b;
            x = Math.floor(x / b);

      zwei Mathematische Funktionen. Das halte ich für uneleganter und mit Sicherheit auch langsamer.

      Aber irgendwie funktioniert die Funktion auch nicht richtig oder ich hab nicht verstanden was der Parameter b sein soll, welche Basis? Die Quersumme ist die Summe aller Ziffern. Aber bei
      alert( quersumme(1234, 10));
      kommt eine Endlosschleife.

      Struppi.

      1. Hallo Struppi*,

        Aber irgendwie funktioniert die Funktion auch nicht richtig oder ich hab nicht verstanden was der Parameter b sein soll, welche Basis? Die Quersumme ist die Summe aller Ziffern. Aber bei alert( quersumme(1234, 10)); kommt eine Endlosschleife.

        Daniel hat da einen Flüchtigkeitsfehler gemacht und vergessen, die Summe pro Iteration wieder auf Null zu setzen. Konsequenterweise wird mit einer immer größer werdenden Summe gerechnet. So funktioniert es:

        ~~~javascript function quersumme(x, b) {
              while (x >= b) {
                  var sum = 0;
                  while (x > 0) {
                      sum += x % b;
                      x = Math.floor(x / b);
                  };
                  x = sum;
              };
              return sum;
          };

          
          
        
        > Naja. ... du hast zwei Schleifen ... zwei Mathematische Funktionen. Das halte ich für uneleganter und mit Sicherheit auch langsamer.  
          
        Wieso das denn? Computer sind auf Berechung ausgelegt, mathematische Funktionen lassen sich da wunderbar in wenig Maschinencode übersetzten und sind da also schneller als ständiges Stringsplitting, Listenmanipuliation und Typumwandlung.  
          
        Auch Rekursion ist oft ineffizienter als eine Schleife – bei Rekursion muss ein neuer Kontext für jeden Funktionsaufruf angelegt werden, von denen es ja nicht gerade wenig gibt. Eleganter ist Rekursion aber, ja.  
          
          
        Ich konnte mich nicht zurückhalten, und mal alle Permutationen von Rekursion vs. Iteration und Mathematische Lösung vs. Array/String-lösung konkret auf die wiederholte Ablaufgeschwindigkeit zu testen:  
          
          ~~~javascript
        #!/usr/bin/env js  
          
          var ITERATIONS = 1000000;  
          
          // Helfermethödchen  
          Number.prototype.times = function (func) {  
              for (var i = 1; i < this +1 ; i++) {  
                  func(i)  
              };  
          };  
          
          // Alle Lösungen praktisch in einem Array  
          var solutions = [  
              { desc : "a) Iterativ (klassische for-Schleife), mit ständiger Umwandlung in Strings",  
                func : function (number) {  
                           var digits = number.toString().split('');  
                           while (digits.length > 1) {  
                               var sum = 0;  
                               for (var i=0; i < digits.length; i++) {  
                                  sum += parseInt(digits[i]);  
                               };  
                               digits = sum.toString().split('');  
                           };  
                           return digits[0];  
                       }  
              },  
              { desc : "b) Iterativ (JS 1.6 Array.forEach), mit ständiger Umwandlung in Strings",  
                func : function (number) {  
                           var digits = number.toString().split('');  
                           while (digits.length > 1) {  
                               var sum = 0;  
                               digits.forEach(function (d) {  
                                   sum += parseInt(d);  
                               });  
                               digits = sum.toString().split('');  
                           };  
                           return digits[0];  
                       }  
              },  
              { desc : "c) Rekursiv, mit ständiger Umwandlung in Strings",  
                func : function (number) {  
                           var self = solutions[2].func  
                           var digits = number.toString().split("");  
                           var sum = 0;  
                           for (var i=0; i < digits.length; i++) {  
                               sum += parseInt(digits[i]);  
                           };  
                           return (sum > 9) ? self(sum) : sum;  
                       }  
              },  
              { desc : "d) Iterativ, mit mathematischen Funktionen",  
                func : function (number) {  
                           while (number > 9) {  
                               var sum = 0;  
                               var x = number;  
                               while (x > 0) {  
                                   sum += x % 10;  
                                   x = Math.floor(x / 10);  
                               };  
                               number = sum  
                           };  
                           return number;  
                       }  
              },  
              { desc : "e) Rekursiv, mit mathematischen Funktionen",  
                func : function (number) {  
                           var self = solutions[4].func;  
                           if (number > 9) {  
                               var lastDigit = number % 10;  
                               number = Math.floor(number / 10);  
                               return self(self(number) + lastDigit);  
                           } else {  
                               return number;  
                           };  
                       }  
              }  
          ];  
          
          
          var tests = [  
              { number  : 9631,  
                digitsum : 1      },  
              { number   : 12345,  
                digitsum : 6      },  
              { number   : 11,  
                digitsum : 2      },  
              { number   : 23,  
                digitsum : 5      },  
              { number   : 123,  
                digitsum : 6      },  
              { number   : 9,  
                digitsum : 9      }  
          ];  
          
          // Sanity-Checking  
          solutions.forEach( function (sol) {  
              if(!(tests.every(function (test) {  
                  return sol.func(test.number) == test.digitsum;  
              }))) {  
                  throw 'Fehler in der Lösung "' + sol.desc + '"';  
              };  
          });  
          
          
          // Und nun das konkrete Testen  
          
          function timeIt (sol) {  
              print(sol.desc);  
              var start = new Date();  
              (3).times(function (i) {  
                  print("Iteration " + i);  
                  ITERATIONS.times(sol.func);  
              });  
              var end = new Date();  
              // Messfehlerverringerung  
              return Math.floor((end -start) / 3);  
          };  
          
          var results = solutions.map(function (sol) {  
              return [sol.desc, timeIt(sol)];  
          });  
          
          results.sort(function (a, b) { return a[1] - b[1]; });  
          
          
          (2).times(function () {print()});  
          print("Für " + ITERATIONS + " Iterationen brauchen die diversen Lösungen sortiert:");  
          results.forEach(function (result) {  
              print(result[0] + " : " + result[1] + "ms (im Durchschnitt: " + (result[1]/ITERATIONS) + "ms)");  
          });
        

        Da ich etwas viel Spaß mit Mozillas Array-Methoden aus JS 1.6 hatte (Sorry ;), hab ich noch Variante b) dazugemischt, um den Vergleich on Array.forEach zu einer normalen For-Schleife zu haben. Und bei Variante e) hab ich die Rekursion eventuell etwas übertrieben.

        Gerne hätte ich noch eine Lösung mit Array.reduce gebastelt, aber in meinem Spidermonkey hatte ich nur JS 1.6 zur Verfügung.

        Die Resultate:

        Für 1000000 Iterationen brauchen die diversen Lösungen sortiert:
        d) Iterativ, mit mathematischen Funktionen : 7191ms (im Durchschnitt: 0.007191ms)
        e) Rekursiv, mit mathematischen Funktionen : 14124ms (im Durchschnitt: 0.014124ms)
        c) Rekursiv, mit ständiger Umwandlung in Strings : 35606ms (im Durchschnitt: 0.035606ms)
        a) Iterativ (klassische for-Schleife), mit ständiger Umwandlung in Strings : 41591ms (im Durchschnitt: 0.041591ms)
        b) Iterativ (JS 1.6 Array.forEach), mit ständiger Umwandlung in Strings : 69194ms (im Durchschnitt: 0.069194ms)

        Klarer Gewinner: Iterativ & Mathematisch.

        Ich war zuerst überrascht, dass die rekursiven Varianten doch so gut sind. Mit einer zweiten Überlegung liegt das aber wohl daran, dass ich als Berechnungszahlen nur Zahlen mit 6 Stellen angegeben haben – da sind einfach nicht so viele Ziffern, die größere Rekursionstiefen erfordern würden. Allerdings hatte ich bei einigen Versuchen mit größeren Zahlen schon exponential-basierte Rundungsfehler ab 17 Ziffern – bei Quersummen ist das nicht so sonderlich toll.

        Was mich dann doch etwas aus den Socken gehauen hatte, war das schlechte Abschneiden der iterativen Lösung mit Array.forEach. Klar, durch den iterativen Ansatz könnte es nicht parallel arbeiten (falls in Spidermonkey Unterstützung dafür vorhanden ist); aber ich hätte schon gedacht, dass der native Code einen Hauch schneller bei der Abarbeitung wäre, als der durch die for-Schleife erzeugte Syntaxtree/Bytecode/Whatever.

        Vielleicht möchte jemand anderes noch weiter experimentieren.

        Tim*

        --
        * Es fehlt ein Haddock. Eindeutig.
        1. Naja. ... du hast zwei Schleifen ... zwei Mathematische Funktionen. Das halte ich für uneleganter und mit Sicherheit auch langsamer.

          Wieso das denn? Computer sind auf Berechung ausgelegt, mathematische Funktionen lassen sich da wunderbar in wenig Maschinencode übersetzten und sind da also schneller als ständiges Stringsplitting, Listenmanipuliation und Typumwandlung.

          Naja, Modulo ist ja eine Division die ja bekanntlich die teuersten Operationen auf einem Rechner sind, während Strings ja nichts weiter als Zeiger auf Speicherketten sind, die eigentlich recht schnell durchlaufen werden können. Aber meine Erfahrungen mit Maschinencode stammen noch aus einer Zeit, als gerade die Math Coprozessoren Standard wurden, da hat sich wohl sehr viel getan ;-)

          Auch Rekursion ist oft ineffizienter als eine Schleife – bei Rekursion muss ein neuer Kontext für jeden Funktionsaufruf angelegt werden, von denen es ja nicht gerade wenig gibt. Eleganter ist Rekursion aber, ja.

          Das hatte ich tasächlich verdrängt.

          Ich konnte mich nicht zurückhalten, und mal alle Permutationen von Rekursion vs. Iteration und Mathematische Lösung vs. Array/String-lösung konkret auf die wiederholte Ablaufgeschwindigkeit zu testen:

          Sehr schön. Mit zwei kleinen Änderungen kann man sich das auch im Browser anschauen:

          Die print funktion muss implementiert werden:

          function print(txt)  
          {  
          if(!txt) txt = '<br>';  
          document.write('<pre>' + txt  +'</pre>');  
          }  
          
          

          und die Anzahl muss auf ca. 5000 beschränkt werden.

          Gerne hätte ich noch eine Lösung mit Array.reduce gebastelt, aber in meinem Spidermonkey hatte ich nur JS 1.6 zur Verfügung.

          in meinem FF gibt es die Methode leider auch noch nicht.

          Klarer Gewinner: Iterativ & Mathematisch.

          Tja, man kann ja nicht immer recht haben ;-)

          Struppi.

          1. Hallo Struppi,

            Naja, Modulo ist ja eine Division die ja bekanntlich die teuersten Operationen auf einem Rechner sind, während Strings ja nichts weiter als Zeiger auf Speicherketten sind, die eigentlich recht schnell durchlaufen werden können.

            Das Problem ist, dass Du erstmal aus der binärzahl einen String machen musst. Der Algorithmus für solche Umwandlungen macht im wesentlichen diese modulo-Operationen. Damit braucht das erzeugen des Strings allein schon mindestens so viel Operationen, wie die eigentliche Rechnung. Um die einzelnen Stellen zu summieren müssen dann Zeichen bzw ein Zeichen lange Strings, weil Javascript da wahrscheinlich gar nicht groß unterscheidet, wieder in Zahlen gewandelt werden. Außerdem braucht so ein String natürlich mehr Speicher als so eine Zahl, die Zahl passt in ein oder zwei Register, bei Strings dürfte da viel mehr rumkopiert werden müssen. Wobei man Aussagen auf der Ebene bei Scriptsprachen schlecht treffen kann, weil man nie so genau weiß, was ein Interpreter so alles tut um etwas auszuführen... Deswegen war ich auch vorsichtig mit Geschwindigkeitaussagen. Es könnte durchaus mal passieren, dass in der Scriptsprache irgendwas so langsamer ist, dass die Stringoperation, die in irgendwelchen Bibliotheken implementiert sind, schneller sind.

            Grüße

            Daniel

        2. Hallo,

          var self = solutions[2].func

          Du suchst arguments.callee. :)

          Mathias

          1. Hallo Mathias,

            Du suchst arguments.callee. :)

            Jetzt nicht mehr. Danke. ;)

            Tim

        3. Hallo Tim,

          Da hast du dir ja richtig Mühe gemacht, Respekt!
          Solche Quersumen hab ich auch mal berechnet, deshalb interessiert mich das.

          Persönlich finde ich – Performance hin oder her – die elganteste Lösung immer die beste. Es sei denn, man hat einen wichtigen Grund, unbedingt auf Tempo zu optimieren.

          Die eleganteste Lösung scheint mir hier die Rekursion mit Umwandlung in Strings, aber bitte nicht zu umständlich. Es genügt eigentlich ein Zweizeiler (qs=Quersumme, n=Number, a=array):

          function qs(n,a) {

          while ( ( a||[] )[0] ) {

          n += parseInt( a.pop() );
           }
           return (n < 10) ? n : qs( 0, n.toString().split('') );
          }

          Wenn das nicht elegant ist...

          Don P

          1. Hallo donp,

            Die eleganteste Lösung scheint mir hier die Rekursion mit Umwandlung in Strings, aber bitte nicht zu umständlich.

            Die Umwandlung ist unelegant, weil Strings eine wirklich platzaufwendige Zahlendarstellung darstellung und rechenoperationen darauf im vergleich wirklich katastrophal langsam sind. Ich lehne es auch immer ab, über irgendwelche minimalen Performance-Unterschiede zu diskutieren. Irgendwelche Hacks zu verwenden und damit unwartbare Programme zu erzeugen, um irgendwelche hypothetischen Verbesserungen herauszuholen, ist natürlich schwachsinnig.
            Wenn man nur eine quersumme ausrechnen will, ist es auch egal, wie man es tut. Meistens verwendet man solche rechenoperationen aber in einem Kontext, in dem man sie sehr häufig benötigt. Und dann wird der unterschied durchaus relevant.
            Ein weiterer Hintergrund, warum ich die String-Variante für unelegant halte, ist, dass man damit auf einer Darstellung der Zahlen und nicht auf den Zahlen selbst arbeitet.

            function qs(n,a) {

            while ( ( a||[] )[0] ) {

            n += parseInt( a.pop() );
            }
            return (n < 10) ? n : qs( 0, n.toString().split('') );
            }

            Nein, wenn man weiß, was da alles abläuft, ist das nicht mehr elegant. Hinter split steckt eine Implementierung von Regulären ausdrücken, parseInt() hat sicher auch ein paar Zeilen Code. Muss man wirklich so dicke Geschütze auffahren, um ein bisschen zu rechnen?
            Außerdem verwendest Du dann auch noch Tricks wie "a||[]" und eine while-Schleife um über ein Array zu iterieren. Das ist nicht besonders lesbar (wer kennt schon die genaue Semantik von || in JS) und eine while-Schleife dürckt auch nicht gleich aus, dass hier einfach ein Array durchlaufen wird.
            Dann wird beim ersten Aufruf auch noch die while-Schleife gar nicht ausgeführt, d.h. der erste Rekursionsschritt tut erstmal nichts.
            Du hast einen Parameter a, der nur für interne Zwecke dient.
            Nene, tut mir Leid ;-) aber das ist meiner Meinung eher die uneleganteste genannte Lösung.

            Grüße

            Daniel

            1. Hallo Daniel,

              Jetzt war ich soooo stolz auf meine geniale Lösung, und du machst sie dermaßen runter. Das ist ja starker Tobak.

              Die Umwandlung ist unelegant, weil Strings eine wirklich platzaufwendige Zahlendarstellung darstellung und rechenoperationen darauf im vergleich wirklich katastrophal langsam sind.

              Dass Rechnen mit Zahlen viel schneller geht bezweifelt niemand.
              Aber von Eleganz im Programmcode scheinen wir gänzlich unterschiedliche Vorstellungen zu haben.

              Ein weiterer Hintergrund, warum ich die String-Variante für unelegant halte, ist, dass man damit auf einer Darstellung der Zahlen und nicht auf den Zahlen selbst arbeitet.

              Das ist ja das selbe Argument wie oben.

              function qs(n,a) {

              while ( ( a||[] )[0] ) {

              n += parseInt( a.pop() );
              }
              return (n < 10) ? n : qs( 0, n.toString().split('') );
              }
              Nein, wenn man weiß, was da alles abläuft, ist das nicht mehr elegant. Hinter split steckt eine Implementierung von Regulären ausdrücken, parseInt() hat sicher auch ein paar Zeilen Code.

              Natürlich ist das so. Wieder das selbe Argument.

              Muss man wirklich so dicke Geschütze auffahren, um ein bisschen zu rechnen?

              Man *muss* nicht, aber es ist eben eleganter in meinen Augen, weil kompakt mit wenig Quellcode.

              Außerdem verwendest Du dann auch noch Tricks wie "a||[]" und eine while-Schleife um über ein Array zu iterieren. Das ist nicht besonders lesbar (wer kennt schon die genaue Semantik von || in JS) und eine while-Schleife dürckt auch nicht gleich aus, dass hier einfach ein Array durchlaufen wird.

              Dass ein pop() in einer while-Schleife schrittweise Array-Elemente entfernt, sieht doch ein Blinder ;)

              "a||[]" ist kein Trick, sondern ein elegantes Konstrukt, das absichtlich in JavaScript implementiert wurde. Es ist die Abkürzung für
              if (a) { return a; }
              else{ return new Array(); }

              Warum sollte man auf solchen "Sugar" verzichten, nur weil viele nicht wissen, wie der || -Operator funktioniert? Der funktioniert überall gleich in JavaScript (auch JS), entsprechend der ECMAScript Spezifikation.

              Dann wird beim ersten Aufruf auch noch die while-Schleife gar nicht ausgeführt, d.h. der erste Rekursionsschritt tut erstmal nichts.

              Das ist doch kein Rekursionsschritt, denn wenn die erste übergebene Zahl bereits einstellig ist, passiert wirklich nichts: Sie wird direkt zurückgegeben. Willst du denn auf eine Zahl <10 noch irgendwelche Rechenoperationen anwenden, bevor du merkst, dass du schon lange am Ziel bist?

              Du hast einen Parameter a, der nur für interne Zwecke dient.

              Genau, möchte nicht wissen, wieviele Variablen du für deine mathematischen Operationen brauchst. Mir reichen insgesamt 2.

              Und noch etwas: Der vermeintliche Zeitgewinn mit nur Zahlen ist in JavaScript nicht besonders groß, wenn es um Integer-Werte geht, wie in diesem Fall. JavaScript kennt nämlich überhaupt kein Integer, sondern nur 32Bit Floatingpoint. D.h. auch das Rechnen mit ganzen Zahlen passiert intern immer im Floatingpoint-Format.

              Nene, tut mir Leid ;-) aber das ist meiner Meinung eher die uneleganteste genannte Lösung.

              Wir haben halt unterschiedliche Vorstellungen von Eleganz. Was du meinst ist Schnelligkeit, Effizienz zur Laufzeit oder was immer. Ich dagegen meine Übersichtlichkeit u. Schlankheit im Code. Rekursion frisst immer etwas mehr Speicher usw., ist aber eben eleganter, weil derselbe Code einfach wiederverwendet werden kann.

              Gruß, Don P

              1. Hallo,

                Jetzt war ich soooo stolz auf meine geniale Lösung, und du machst sie dermaßen runter. Das ist ja starker Tobak.

                ja, es ist wenig einfühlsam - aber irgendwie trotzdem treffend.

                Aber von Eleganz im Programmcode scheinen wir gänzlich unterschiedliche Vorstellungen zu haben.

                Scheint so.

                Man *muss* nicht, aber es ist eben eleganter in meinen Augen, weil kompakt mit wenig Quellcode.

                Nee, hast du mal den Quellcode angesehen, der sich hinter den von dir aufgerufenen Funktionen verbirgt? Der gehört natürlich mitgerechnet!

                "a||[]" ist kein Trick, sondern ein elegantes Konstrukt, das absichtlich in JavaScript implementiert wurde. Es ist die Abkürzung für
                if (a) { return a; }
                else{ return new Array(); }

                Danke, das war auch mir nicht ganz klar. Die Array-Klammern [] ohne Operand davor sahen für mich ziemlich verunglückt aus; mir war nicht bekannt, dass Javascript auf diese Weise auch anonyme Arrays kennt.

                Warum sollte man auf solchen "Sugar" verzichten, nur weil viele nicht wissen, wie der || -Operator funktioniert? Der funktioniert überall gleich in JavaScript (auch JS), entsprechend der ECMAScript Spezifikation.

                Der war mir geläufig, aber die alleinstehenden Klammern dahinter habe ich beim ersten Lesen als Bullshit eingeordnet.

                Und noch etwas: Der vermeintliche Zeitgewinn mit nur Zahlen ist in JavaScript nicht besonders groß, wenn es um Integer-Werte geht, wie in diesem Fall. JavaScript kennt nämlich überhaupt kein Integer, sondern nur 32Bit Floatingpoint. D.h. auch das Rechnen mit ganzen Zahlen passiert intern immer im Floatingpoint-Format.

                Ja, das habe ich auch schon manchmal verflucht.

                Wir haben halt unterschiedliche Vorstellungen von Eleganz. Was du meinst ist Schnelligkeit, Effizienz zur Laufzeit oder was immer.

                So würde ich Eleganz bei Programmcode auch sehen.

                Ich dagegen meine Übersichtlichkeit u. Schlankheit im Code.

                Ich auch. Und das ist kein Widerspruch zum vorherigen Satz: Funktionsaufrufe, die viel Code implizieren, sind diesem Ideal für meine Begriffe abträglich.

                Rekursion frisst immer etwas mehr Speicher usw., ist aber eben eleganter, weil derselbe Code einfach wiederverwendet werden kann.

                Einverstanden - mir geht es auch, genau wie Daniel, nicht um die Rekursion und den damit verbundenen Stackbedarf, sondern um die Menge an zusätzlichem Code, die sich hinter einem unscheinbaren Funktionsaufruf versteckt.

                So long,
                 Martin

                --
                Wissen erwirbt man, indem man immer das Kleingedruckte sorgfältig liest.
                Erfahrung bekommt man, indem man das nicht tut.
                1. Hallo Martin,

                  Wenigstens du hast ein bisschen Verständnis für meine Argumente, danke.

                  Nee, hast du mal den Quellcode angesehen, der sich hinter den von dir aufgerufenen Funktionen verbirgt? Der gehört natürlich mitgerechnet!

                  Alles was dahinter abläuft, ist eigentlich kein Quellcode mehr, gehört also nicht zwingend mitgerechnet. Es ist mir natürlich bewusst, dass hier evtl. mit Kanonen auf Spatzen geschossen wird.

                  Unter "Eleganz" des Codes verstehe ich so etwas wie Schönheit im Ausdruck und der Grammatik, so wie man z.B. sagt "im Sonnenlicht" anstatt "in dem Licht der Sonne".

                  Auch in jeder gesprochenen Sprache setzt sich immer das einfachere, kürzere und schönere durch. Nicht umsonst wurden Dichter wie Goethe berühmt, sondern weil sie mit ganz wenigen Worten sehr viel ausdrücken konnten. Wenn Goethe z.B. schreibt "Die Ungewissheit schlägt mir tausendfach die dunklen Schwingen um das bange Haupt", dann hat er mit nur 12 Worten ein gigantisches Bild hingemalt, für das andere lange Sätze von Konstanz bis Flensburg machen würden ;)).
                  Manch einer, der der Sprache weniger mächtig ist oder einfach weniger Sinn dafür hat, könnte z.B. argumentieren "muss man denn solche Geschütze auffahren, nur um zu sagen, dass einem wegen einer Unklarheit sehr Unwohl ist?"

                  Aber ich schweife ab, zurück zu JavaScript:

                  Die Array-Klammern [] ohne Operand davor sahen für mich ziemlich verunglückt aus; mir war nicht bekannt, dass Javascript auf diese Weise auch anonyme Arrays kennt.

                  Das weiß ich auch noch nicht lange. Bin gerade dabei solche Feinheiten zu lernen. Jeder programmiert heutzutage in JavaScript, aber kaum jemand hat es von der Pike auf gelernt bzw. das Besondere der Sprache wirklich verstanden. In jeder anderen Programmiersprache wäre das undenkbar.

                  Also statt "new Array()" schreibt man besser immer "[]", und statt "new Object()" besser nur "{}". Der Compiler versteht's auf Anhieb, und überhaupt kann bzw. sollte man auf den "new" Operator weitgehend verzichten. Statt "new String('Zeichenkette')" also einfach nur 'Zeichenkette' usw. schreiben.

                  Richtig fies kann's mit "new Boolean()" werden, man nehme z.B.

                  var bool = new Boolean(false);
                  if (bool) { doSome(thing); }

                  Hier würde man eigentlich erwarten, dass "doSome(thing)" nicht ausgeführt wird, da "bool" zuvor mit false initialisiert wurde. doSome(thing) kommt aber dennoch zum Zug, weil der new-Operator immer einen Objekt-Wrapper um die Variable baut. "bool" ist also ein existierendes Objekt, und "if (bool)..." testet jetzt auf die Existenz des Objekts, nicht auf den Wert der Variablen.
                  Das Problem kann nicht auftreten, wenn man statt dessen schreibt

                  var bool = false;
                  if (bool)...

                  Gruß, Don P

                  1. Hallo,

                    Nee, hast du mal den Quellcode angesehen, der sich hinter den von dir aufgerufenen Funktionen verbirgt? Der gehört natürlich mitgerechnet!
                    Alles was dahinter abläuft, ist eigentlich kein Quellcode mehr, gehört also nicht zwingend mitgerechnet.

                    so betrachtet stimmt das natürlich, aber ich sehe, wenn ich ein Stück Programmcode anschaue, nicht unbedingt nur den Quellcode, sondern "denke" automatisch auf Maschinenebene, und sehe den _ausgeführten_ Code unter diesem Aspekt. "Elegant" heißt daher für mich, möglichst kompakt im Hinblick auf die auszuführende Operation zu formulieren, und zumindest für mich wird der Programmcode durch diese Kompaktheit meist auch leichter lesbar, verständlicher.

                    Unter "Eleganz" des Codes verstehe ich so etwas wie Schönheit im Ausdruck und der Grammatik, so wie man z.B. sagt "im Sonnenlicht" anstatt "in dem Licht der Sonne".

                    Ja schon, aber um bei deinem Vergleich zu bleiben: Funktionsaufrufe würde ich dann mit eingeschobenen Nebensätzen vergleichen. Das ist zwar praktisch und übersichtlich, aber nicht immer "elegant", und ergibt oft unnötig lange Sätze.

                    Wenn Goethe z.B. schreibt "Die Ungewissheit schlägt mir tausendfach die dunklen Schwingen um das bange Haupt", ...

                    dann würde man das heute vielleicht kürzer und knapper formulieren: "Dieses Warten macht mich wahnsinnig."

                    Die Array-Klammern [] ohne Operand davor sahen für mich ziemlich verunglückt aus; mir war nicht bekannt, dass Javascript auf diese Weise auch anonyme Arrays kennt.
                    Das weiß ich auch noch nicht lange. Bin gerade dabei solche Feinheiten zu lernen. Jeder programmiert heutzutage in JavaScript, aber kaum jemand hat es von der Pike auf gelernt bzw. das Besondere der Sprache wirklich verstanden. In jeder anderen Programmiersprache wäre das undenkbar.

                    In C ist sowas in der Art durchaus auch möglich - anonyme Funktionen, Zeiger, Arrays (die in C intern ja über Zeiger adressiert werden), Strukturen, ...

                    Also statt "new Array()" schreibt man besser immer "[]", und statt "new Object()" besser nur "{}".

                    Ich bin schon überzeugt. ;-)

                    Schönes Wochenende,
                     Martin

                    --
                    Wer morgens zerknittert aufsteht, hat den ganzen Tag Gelegenheit, sich zu entfalten.
            2. Hi Daniel,

              Habe mir gerade nochmal deine Lösung oben angesehen, und ich muss sagen, die ist wirklich gut. Sogar die Basis kann man frei wählen, wow! An Eleganz im Quellcode stellt *meine* sie aber in den Schatten :)

              Du musst gleich 2 while-Schleifen bemühen, "teure" Divisionen, Modulo-Operationen und Math.floor- Berechnungen durchführen, alles mit 32Bit-Floatingpoint intern, naja...

              Man sollte unsere beiden Lösungen mal gegeneinander antreten lassen, nur um zu sehen, wie groß dein vermeintlicher Tempo-Vorteil wirklich ist.

              Wenn du auf höchste Effizienz abzielst, ist JavaScript eh nicht die richtige Sprache für dich. Dann solltest du dich lieber mit Assembler beschäftigen :).

              Gruß, Don P

              1. Man sollte unsere beiden Lösungen mal gegeneinander antreten lassen, nur um zu sehen, wie groß dein vermeintlicher Tempo-Vorteil wirklich ist.

                Warum machst du es nicht, der Code ist doch schon vorhanden, du musst nur deine Funktion integrieren. (und um das im Browser zu testen, meine Hinweise einbauen)

                Struppi.

                1. Man sollte unsere beiden Lösungen mal gegeneinander antreten lassen, nur um zu sehen, wie groß dein vermeintlicher Tempo-Vorteil wirklich ist.

                  Warum machst du es nicht, der Code ist doch schon vorhanden, du musst nur deine Funktion integrieren. (und um das im Browser zu testen, meine Hinweise einbauen)

                  Habe gerade deinen Hinweis gefunden, dass die Daniel's Lösung ja gar nicht richtig funktioniert. Es stimmt tatsächlich, das u.U. eine Endlosschleife entsteht. Wie uncool :) !!
                  Sonst hätte ich jetzt mal die Probe auf's Exempel gemacht, aber wenn man dazu erst seine Funktion reparieren muss...

                  1. Sonst hätte ich jetzt mal die Probe auf's Exempel gemacht, aber wenn man dazu erst seine Funktion reparieren muss...

                    ich meinte den Benchmarktest hier https://forum.selfhtml.org/?t=158543&m=1031679
                    Da kannst du ja deinen Code mal einbauen und schauen wie's mit der Geschwindigkeit aussieht.

                    Struppi.

                    1. Sonst hätte ich jetzt mal die Probe auf's Exempel gemacht, aber wenn man dazu erst seine Funktion reparieren muss...

                      ich meinte den Benchmarktest hier https://forum.selfhtml.org/?t=158543&m=1031679
                      Da kannst du ja deinen Code mal einbauen und schauen wie's mit der Geschwindigkeit aussieht.

                      Das hab' ich probiert, und wie zu erwarten war, ist meine Funktion mit Abstand die langsamste :(.

                      Aber da da sie euch ja eh nicht elegant genug ist, wie wär's damit:

                      function qs(n){return n%9||9;}

                      Zufrieden? Das ist jetzt wohl wirklich nicht mehr zu toppen, meine ich :))

                      Wenn man ein bisschen darüber nachdenkt, stellt man bald fest, das die einstellige Quersumme genau der Funktion Modulo 9 entspricht, mit dem einzigen Unterschied, dass 9n modulo 9 = 0  für alle natürlichen n > 0 gilt, wir brauchen dann aber eine 9, was mit dem Operator || leicht zu machen ist.

                      Gruß, Don P

                  2. Hallo donp,

                    Man sollte unsere beiden Lösungen mal gegeneinander antreten lassen, nur um zu sehen, wie groß dein vermeintlicher Tempo-Vorteil wirklich ist.

                    Tim hat ja verschiedene Varianten getestet. Die schnellste Variante mit Strings braucht fast fünf mal so viel Zeit wie meine. Ich bin da also mal recht optimistisch.

                    Habe gerade deinen Hinweis gefunden, dass die Daniel's Lösung ja gar nicht richtig funktioniert. Es stimmt tatsächlich, das u.U. eine Endlosschleife entsteht. Wie uncool :) !!

                    Ja, diesen kleinen Fehler hat Tim schon korrigiert, bevor ich das tun konnte. Vielleicht liest Du einfach erstmal die ganze Diskussion bevor Du sie Stückchenweise entdeckst ;-)

                    Nochmal zur Frage der Elleganz.
                    Elleganz ist sicher nicht direkt Ausführungsgeschwindigkeit, um etwas verständlich, erweiterbar o.ä. umzusetzen, kann man schon Ausführungsgeschwindigkeit opfern.
                    Ausführungsgeschwindigkeit ist dann ein Kriterium, wenn sich das asymptotische Verhalten ändert (passiert hier wahrscheinlich nicht) oder die Rechenzeit sich wirklich um einen relevanten Faktor unterscheidet. Was relevant ist, ist natürlich eine Frage der Anwendung. Bei einem Faktor > 2 wird es aber schon schnell relevant.

                    Immer ein Kriterium ist aber Einfachheit. Einfachheit ist dabei nicht zu verwechseln mit kürze. Man kann eben Einzeiler schreiben (in manchen Sprachen deutlich besser als in JS), in denen so viel ausgedrückt wird, dass 10 oder gar 100 Zeilen Code wahrscheinlich besser gewesen wären.

                    Von der Komplexität des Algorithmuses geben sich Dein und mein Programm nichts. Statt meiner äußeren Schleife verendest Du einen rekursiven Aufruf am Ende, die nichts anderes ausdrückt. Die while-Schleife drückt das meiner Meinung nach direkter aus, aber als rekursionsgewohnter Programmierer kommt man natürlich mit beidem klar.
                    Das aufsummieren der einzelnen Stellen machen wir beide mit einer while-Schleife. Der unterschied der bleibt ist lediglich String <-> Mathematik. Wenn man weiß, wie man Zahlendarstellungen ineinander umrechnet, (nämlich mit eben jener modulo-Operation) springt einen da meine Lösung an. Die String-Operationen ersparen einem an der Stelle eben, zu verstehen, was eigentlich passiert.
                    Gestört hat mich übrigens nicht die Verwendung von [], Array-Literale gibt es in vielen Sprachen, sondern die von ||. || ist erstmal ein boolsches Oder, die Verwendung als eine Art Inline-IF ist nicht besonders Erwartungskonform.

                    Eine sauber implementierte Variante Deiner vorgehensweise wäre:
                    function quersumme(n) {
                      var s = n.toString();
                      var sum = 0;
                      for (var i = 0; i < s.length; ++i) {
                        sum += s[i];
                      }
                      return sum < 10 ? sum : quersumme(sum);
                    }
                    Braucht zwei Zeilen mehr, iteriert ganz standardmäßig und verwendet keine überflüssigen Funktionsparameter, die beim Aufruf richt initialisiert werden müssen.

                    Grüße

                    Daniel

                    1. function quersumme(n) {
                        var s = n.toString();
                        var sum = 0;
                        for (var i = 0; i < s.length; ++i) {
                          sum += s[i];

                      funktioniert das jetzt in allen Browsern?
                      Ich kannte das bisher vom IE

                      Was aber auf jeden Fall noch fehlt ist ein parseInt(s[i])

                      Struppi.

                    2. Gestört hat mich übrigens nicht die Verwendung von [], Array-Literale gibt es in vielen Sprachen, sondern die von ||. || ist erstmal ein boolsches Oder, die Verwendung als eine Art Inline-IF ist nicht besonders Erwartungskonform.

                      Doch, das ist sie. Die Amis nennen "||" in JavaScript auch den "default operator", weil er eben nicht einfach ein boolesches oder ist, sondern den einen oder den anderen Operanden zrückgibt, welchen Typs diese auch immer sein mögen.

                      "default operator" heißt er deshalb, weil er es erlaubt, den zweiten Operanden als default-Wert zu setzen, falls der erste irgendwie zu "falsch" evaluiert (d.h. "falsy" ist, das muss nicht zwingend false sein, "" oder 0 oder null oder undefined tun´s auch). Das ist auch inline durchaus erlaubt bzw. vorgesehen (warum soll ein "operator" nicht inline verwendet werden?). Solche Dinge machen eine Programmiersprache erst elegant.

                      Nochmal: a||b ist kein boolesches oder, das immer true oder false liefert, sondern es bedeutet nicht mehr und nicht weniger als if(a){return a}else{return b}. Im Spezialfall mit zwei booelschen Werten a und b enspricht das natürlich dem booleschen oder, aber das ist, wie gesagt, nur ein Spezialfall.

                      Mein Ausdruck a||[])[0] bedeutet die Existenz (true oder false) des ersten Elements [0] im Array a, welches defaultmässig "||" zum leeren Array wird "[]", falls a nicht definiert sein sollte, was nur beim ersten Aufruf der Funktion eintritt. Das finde ich durchaus elegant ausgedrückt, auch wenn es etwas kryptisch aussieht.
                      Hast du mal ein Perl-Programm gesehen? Eine sehr elegante Sprache, würde ich sagen, aber vom bloßen Hinsehen müsstest du schon Kopfschmerzen bekommen.

                      Eine sauber implementierte Variante Deiner vorgehensweise wäre:
                      function quersumme(n) {
                        var s = n.toString();
                        var sum = 0;
                        for (var i = 0; i < s.length; ++i) {
                          sum += s[i];
                        }
                        return sum < 10 ? sum : quersumme(sum);
                      }

                      Das ist doch nicht dasselbe: In dieser "sauberen" Variante wird die übergebene Zahl erst mal in einen String s umgewandelt. Was bedeutet dann eigentlich s[i]? s müsste doch dabei ein Array sein... Aber das ist gar nicht der Punkt, sondern: Im Fall quersumme(1) wird die 1 erst mal zum String gemacht, dann in ein Array geschickt, dann in der Schleife wieder als Zahl (per automatischer Typumwandlung?) zur 0 addiert, bevor du sie endlich fragst, ob sie nicht vielleicht kleiner als 10 ist. Nicht besonders elegant, würde ich sagen.

                      Meine Funktion macht in diesem Fall nur folgendes: Sie erzeugt ein leeres Array, testet dann ob es ein erstes Element darin gibt (=> natürlich nicht), testet dann sofort, ob sie kleiner als 10 ist (=>natürlich ja), und gibt sie zurück => keinerlei Stringoperationen in diesem Fall, wäre ja auch völlig unnötig.

                      Weiß nicht, ob du mein anderes Posting gelesen hast, wo die ultimative Lösung drin steht, für alle natürlichen Zahlen n>0:

                      function quersumme(n){return n%9||9;}

                      Noch Fragen ;) ?

                      Don P

                      1. Nachtrag:

                        Eine rein mathematische Variante mit frei bählbarer Basis b>0 für alle natürlichen n>0 könnte also, um elegant und vor allem richtig schnell zu sein, so notiert werden:

                        function quersumme( n, b ) { return n < 10 ? n : n%b || b }

                        Don P

                        1. Hallo donp,

                          s[i] ist bei einem String einfach das i. Zeichen. Von daher wird da nichts in ein Array umgewandelt. Über Umwandlungen muss man an der Stelle sowieso nicht mehr diskutieren, wenn man sich entschieden hat, das mit Strings zu machen, dann kann es nur noch um Verständlichkeit gehen, Effizienz hat man sowieso aufgegeben.

                          Was das || angeht. || ist halt ein logisches Oder wie in den meisten Programmiersprachen auch mit etwas extra Semantik. Man kann solche Tricks toll finden, Erwartungskonform sind sie nicht, weil das Zeichen eben idR anders verwendet wird.

                          function quersumme( n, b ) { return n < 10 ? n : n%b || b }

                          Das ist jetzt aber falsch. Wieso n < 10, wenn Du zu einer beliebigen Basis rechnest?

                          Den Zahlentheoretischen zusammenhang auszunutzen ist natürlich wirklich Chic. Müsstest Du nur noch Beweisen (für den Zusammenhang Quersumme/teilbar durch (Basis - 1) <-> Zahl teilbar durch (Basis - 1) habe ich das irgendwann mal gemacht. Für diesen allgemeineren Zusammenhang dürfte das ähnlich klappen, sieht aber etwas fummelig aus.
                          Für andere Arten von Quersummen klappt das aber zum Glück nicht ;-)

                          Grüße

                          Daniel

                          1. Halo Daniel

                            s[i] ist bei einem String einfach das i. Zeichen. Von daher wird da nichts in ein Array umgewandelt.

                            Ach so, hab' ich mir fast gedacht, danke für den Tipp.

                            Über Umwandlungen muss man an der Stelle sowieso nicht mehr diskutieren, wenn man sich entschieden hat, das mit Strings zu machen, dann kann es nur noch um Verständlichkeit gehen, Effizienz hat man sowieso aufgegeben.

                            Das stimmt natürlich, aber eine gewisse Effizienz im Ausdruck gibt es schon. Wenn ich z.B. nicht selber fahren will, kann ich einen Bekannten bitten, dass er mich nach Hause fährt. Das wäre effizient, aber umständlich. Statt dessen kann auch einfach rufen "Taxi!" (koste es was es wolle), das wäre dann elegant.

                            Was das || angeht. || ist halt ein logisches Oder wie in den meisten Programmiersprachen auch mit etwas extra Semantik.

                            Das sehe ich eben anders. Wenn man verlangt, dass es wirklich nur ein logisches Oder ist, muss man extra einen gewissen Aufwand betreiben, nämlich "!!a||!!b" schreiben, statt "a||b".

                            function quersumme( n, b ) { return n < 10 ? n : n%b || b }
                            Das ist jetzt aber falsch. Wieso n < 10, wenn Du zu einer beliebigen Basis rechnest?

                            Äh ja, natürlich. Die Abfrage erübrigt sich eigentlich ohnehin, denn höchstwahrscheinlich ist das bereits im %-Operator mindestens genauso gut implementiert.

                            Den Zahlentheoretischen Zusammenhang auszunutzen ist natürlich wirklich Chic. Müsstest Du nur noch Beweisen (für den Zusammenhang Quersumme/teilbar durch (Basis - 1) <-> Zahl teilbar durch (Basis - 1) habe ich das irgendwann mal gemacht. Für diesen allgemeineren Zusammenhang dürfte das ähnlich klappen, sieht aber etwas fummelig aus.

                            Beweisen kann ich es eigentlich, aber an der mathematischen Ausformulierung hapert's.

                            Ich denke mir das so:

                            1. b sei die größte Ziffer eines Zahlensystems, also z.B. b=9 im Dezimalsystem.

                            2. Die Quersumme ergibt sich durch Addition einzelner Ziffern.
                            Davon gibt es insgesamt b Stück (1...9 = 9 Stück im Dezimalsystem), und die Zero, die aber nicht mitspielt, was die einstellige Quersumme betrifft, weil hier 1 = 10 = 100 usw. gilt. Es gibt nur also b mögliche Ergebnisse.

                            2. Die b möglichen Ergebnisse kann man sich kreisförmig angeordet vorstellen, z.B. auf die 9 im Dezimalsystem folgt wieder die 1 usw. bzgl. der einstelligen Quersumme.

                            3. Dieselbe Position im Keis wird immer dann erreicht, wenn man b Schritte weiter geht, d.h. b addiert oder subtrahiert. b ist also das neutrale Element der Addition bzgl. der einstelligen Quersumme.

                            4. Man kann also von der Ausgangszahl solange b subtrahieren, bis es nicht mehr weiter geht, weil das Ergebnis sonst negativ würde (das macht gerade der Modulo-Operator), denn da b unser neutrales Element der Addition ist, wird unsere Zahl dadurch nicht wirklich verändert. Wir erhalten so die gesuchte einstellige Quersumme, oder aber 0.

                            5. Im Falle von 0 als Rechenergenis der fortgesetzten Subtraktionen ist die Ausgangszahl durch b ohne Rest teilbar. Da die 0 im Kreis unserer möglichen Ergebnisse aber nicht vorkommt, kann man sie als 1 - 1 = b in unserem Kreis betrachten.

                            6. Fazit: Die gesuchte einstellige Quersumme ist entweder der b-Rest der Ausgangszahl n (d.h. n mod b) oder b selbst (falls n nurch b teilbar ist), was zu beweisen war.

                            Gruß, Don P

                          2. Nachtrag:

                            Was das || angeht. || ist halt ein logisches Oder wie in den meisten Programmiersprachen auch mit etwas extra Semantik. Man kann solche Tricks toll finden, Erwartungskonform sind sie nicht, weil das Zeichen eben idR anders verwendet wird.

                            Ok, vielleicht nicht Erwartungskonform, aber nur, weil man dem Operator idR nicht mehr zutraut als eine logische Oder-Verknüpfung, bzw. deshalb, kaum jemand JavaScript "richtig" lernt. Als Progammiersprache wird sie meistens eh unterschätzt. Viele meinen gar, sie sei nicht wirklich objektorientiert.

                            Es ist gerade diese "extra Semantik", die den ||-Operator interessant macht.

                            Statt (a||[])[0] hätte man auch erwartungskonformer (a?a:[])[0] schreiben können, oder auch (a?a[0]:0) bzw. die kürzeste Variante: a&&a[0].

                            Der &&-Operator hat nämlich eine ähnliche Semantik: Während a||b dem Ausdruck a?a:b entspricht, bedeutet a&&b soviel wie a?b:a.

                            && nennt man auch den "guard operator", weil er als Wächter verhindern kann, dass ein ungültiger Wert verarbeitet werden soll:

                            Wenn z.B. a nicht definiert oder null oder false oder NaN ist, dann ist a[0] sicher ein ungültiger Wert, der zu einem Laufzeitfehler führen würde.
                            Um das zu verhindern, kann man mit dem Ausdruck a&&a[0] einen gültigen Wert erzwingen.
                            Denn wenn z.B. a ein Array ist, dann ergibt a&&a[0] immer einen gültigen Wert (das erste Arrayelement), schlimmstenfalls vom Typ 'undefined' beim leeren Array.
                            Sollte a bereits 'undefined' sein, dann ergibt a&&a[0] ebenfalls einen gültigen Wert, diesmal sicher vom Typ 'undefined' (=a selbst).

                            Nochmal zur einstelligen Quersumme:

                            Zu beachten ist dabei, dass die Ausgangszahl n in dezimaler Darstellung übergeben werden muss, wenn der Ausdruck n%b||b für jedes Zahlensystem die richtige einstellige Quersumme liefern soll.

                            Beispiel:
                            Mit n=11 und b=5 im 6er-System scheint das Ergebnis 11%5 = 1 falsch zu sein, weil 1+1 ja nicht 1 ergibt.
                            Da 11d = 1*6 + 5*1 aber die Darstellung "15" im 6er-System hat, ist das Ergebnis 1 korrekt.

                            Gruß, Don P

                      2. Hallo Don,

                        Die Amis nennen "||" in JavaScript auch den "default operator", ...
                        ... es bedeutet nicht mehr und nicht weniger als if(a){return a}else{return b}.

                        also eleganter formuliert eigentlich (a ? a : b), gelle? Das wäre, im Gegensatz zu deiner Umschreibung mit dem if-Statement, dann auch syntaktisch entsprechend, weil es als Ausdruck überall vorkommen darf.

                        Im Spezialfall mit zwei booelschen Werten a und b enspricht das natürlich dem booleschen oder

                        Genaugenommen sogar dem Oder mit "short circuit evaluation", bei dem der zweite Operand gar nicht mehr ausgewertet wird, wenn das Ergebnis ohnehin schon feststeht.

                        Mein Ausdruck a||[])[0] bedeutet die Existenz (true oder false) des ersten Elements [0] im Array a, welches defaultmässig "||" zum leeren Array wird "[]", falls a nicht definiert sein sollte, was nur beim ersten Aufruf der Funktion eintritt. Das finde ich durchaus elegant ausgedrückt, auch wenn es etwas kryptisch aussieht.

                        Für den Fall, dass a nicht definiert ist, will der Ausdruck also das erste Element eines leeren Arrays zurückgeben - ist das denn definiert? ;-)

                        So long,
                         Martin

                        --
                        Finanztipp:
                        Leihen Sie sich Geld von einem Pessimisten.
                        Er rechnet sowieso nicht damit, dass er es zurückbekommt.
                        1. ... es bedeutet nicht mehr und nicht weniger als if(a){return a}else{return b}.

                          also eleganter formuliert eigentlich (a ? a : b), gelle? Das wäre, im Gegensatz zu deiner Umschreibung mit dem if-Statement, dann auch syntaktisch entsprechend, weil es als Ausdruck überall vorkommen darf.

                          Exakt, und wirklich elegant formuliert, könnte von mir sein ;)

                          Für den Fall, dass a nicht definiert ist, will der Ausdruck also das erste Element eines leeren Arrays zurückgeben - ist das denn definiert?

                          ;-)

                          In gewisser Weise ja, es ist ein Wert vom Typ 'undefined', was als Schleifenbedingung ein gültiger Ausdruck ist: Die Bedingung ist dann halt nicht erfüllt.
                          Wenn man nur a[0] schreibt, und a ist bereits vom Typ 'undefined', dann ist a[0] nicht mal mehr das, sondern der ganze Ausdruck ungültig: Es kommt dann zum Laufzeitfehler.

                          Gruß, Don P

        4. Hallo Tim,

          Vielleicht möchte jemand anderes noch weiter experimentieren.

          Ja, das hab ich gemacht, und zwar mit einer noch anderen Variante, die auch mit Stringumwandlung arbeitet, aber sehr sparsam damit ist:

          Die übergebene Zahl wird nur einmalig in einen String gewandelt, und dann wird jede Ziffer wieder einmalig mit parseInt() als Zahl in ein Array geschrieben.

          Alle weiteren Schritte arbeiten mit den numerischen Werten des Arrays und es braucht auch keine Rekursion oder Schleife, die ein Zwischenergebnis erneut behandeln müsste, sondern man kommt mit nur z Additionen für eine z-Stellige Zahl aus. So gesehen sollte es eigentlich schneller gehen, als rekursiv oder iterativ mit ständiger Umwandlung in Strings.

          Die genaue Arbeitsweise ist hier nebensächlich (ergibt sich aus zahlentheoretischen Überlegungen). Hier also die Funktion zum Beweis, dass relativ wenig Stringoperationen benutzt werden:

            
          var qs1 = function (n,r) {             // n = querzusummierende Zahl, r = Radix des Zahlensystems  
            
           r = r||10;                         // Default-Radix ist 10 (Dezimalsystem)  
            
              var a = [],                        // Array für einzelne Ziffern  
                  q = 0;                         // einstellige Quersumme  
            
              n = n.toString(r);                 // Zahl als String im enstsprechenden Zahlensystem  
            
              for ( var d=0; d<n.length; d++ ){  // Mit allen Ziffern:  
            
                  a[d] = parseInt(n[d],r);       //  Array mit numerischen Ziffern füllen  
              }  
            
              r -= 1;                            // Größte Ziffer im Zahlensystem (Radix-1)  
            
              for ( d=0; d<a.length; d++ ) {     // Mit allen Ziffern:  
            
                  if ( q + a[d] > r ){           //  Quersumme würde zweistellig:  
            
                      q -= r-a[d];               //    Um größte Ziffer minus aktuelle Ziffer verkleinern  
            
                  }else{                         //  Quersumme bleibt einstellig:  
            
                      q += a[d];                 //    Um aktuelle Ziffer vergrößern.  
                  }  
              }  
            
              return q;                          // Quersumme zurückgeben.  
          }  
          
          

          Dagegen braucht eine normale rekursive Funktion mit ständiger Umwandlung in Strings mehrere toString(), split() und parseInt():

            
          var qsSR = function (number) {  
           var digits = number.toString().split("");  
           var sum = 0;  
           for (var i=0; i < digits.length; i++) {  
            sum += parseInt(digits[i]);  
           }  
           return (sum > 9) ? qsSR(sum) : sum;  
          }  
          
          

          Wider Erwarten ist diese rekursive Variante mehr String-Operationen aber doppelt so schnell meine vermeintlich optimierte Version!

          Fazit: Performancemäßig viel teurer als Stringumwandlungen sind widerholte Zugriffe auf Array-Elemente. Arrays scheinen wirklich lahme Esel in JacaScript zu sein.

          Gruß, Don P

  5. Das ist mir zu kompliziert.
    Am elegantesten tut's dieser Zweizeiler *brustschwell*:

    function qs(n,a) {
     while ( ( a||[] )[0] ) { n += parseInt( a.pop() ); }
     return (n < 10) ? n : qs( 0, n.toString().split('') );
    }

    qs(n) ergibt die gesuchte einstellige Quersumme
    n = die querzusummierende Zahl
    a = Array

    Gruß, Don P

  6. Die einfachste und schnellste Lösung:

    function qs(n) { return n%9||9; }

    qs(n) ergibt die gesuchte einstellige Quersumme
    n = die querzusummierende Zahl

    Ebenfalls recht knapp formuliert aber umständlich und mit Rekursion:

    function qs(n,a) {
     while ( a&&a[0] ) { n += parseInt( a.pop() ); }
     return (n < 10) ? n : qs( 0, n.toString().split('') );
    }

    qs(n) ergibt die gesuchte einstellige Quersumme
    n = die querzusummierende Zahl
    a = Array