Der fünfhundertfünfundfünfzigste Artikel

Ich möchte hiermit darauf aufmerksam machen, dass dies der fünfhundertfünfundfünfzigste Artikel auf diesem Blog ist.

Nun, was sagt uns diese, ominöse Zahl, fünfhundertfünfundfünfzig? Was findet zum Beispiel Wikipedia dazu? Nun, Wikipedia schlägt unter Anderem das Jahr fünfhundertfünfundfünfzig vor, gefolgt von der Telefonnummer fünfhundertfünfundfünfzig, die in den USA für fiktionale Telefonnummern reserviert zu sein scheint, was der Grund ist, warum diese so oft mit 555 anfangen. Es gibt sogar eine Liste solcher Telefonnummern. Sehr wichtig, das. Ob es wohl auch ein Urheberrecht darauf gibt? Wer weiß. Haben wir in Deutschland sowas eigentlich auch? Wenn nicht muss sofort ein Gesetz erlassen werden. Kann ja nicht sein, dass der Raum der fiktiven Telefonnummern ein rechtsfreier Raum ist.

Jedenfalls haben wir in Deutschland eine Bundesautobahn 555, und die deutsche Wikipedia kennt außerdem noch einen Schaltkreis. Naja, wie lange noch, ist natürlich die Frage. Denn zweifelsohne ist das alles nicht relevant, wird also bestimmt irgendwann gelöscht.

In der englischen Wikipedia erfährt man noch, dass fünfhundertfünfundfünfzig diejenige natürliche Zahl ist, die zwischen fünfhundertvierundfünfzig und fünfhundertsechsundfünfzig ist. Und eine Sphenische Zahl ist, also eine Zahl, die sich als Produkt von drei Primzahlen in der ersten Potenz schreiben lässt, in diesem Falle drei, fünf und siebenunddreißig. Außerdem ist sie eine Harshad-Zahl, also eine Zahl die durch ihre Quersumme teilbar ist, bezüglich der Basen 2, 10, 11, 13 und 16, die jeweiligen Darstellungen sind 1000101011(2), 555(10), 465(11), 339(13), 22B(16), die Quersummen also fünf und fünfzehn. Soweit Wikipedia. Genauer ist da das folgende Programm, das nicht unbedingt effizient geschrieben ist, aber als schneller Hack mit Gedankenminimierung ausreicht:

clisp -x ‚(defun basednum-to-int (base digits) (if digits (+ (car digits) (* base (basednum-to-int base (cdr digits)))) 0)) (defun basednum-inc (base digits) (if digits (if (< (car digits) (1- base)) (cons (1+ (car digits)) (cdr digits)) (cons 0 (basednum-inc base (cdr digits)))) (list 1))) (defun convert-to-base (base int &optional (sofar nil)) (if (equal int (basednum-to-int base sofar)) sofar (convert-to-base base int (basednum-inc base sofar)))) (defun sum-of-digits (base int) (let ((ret 0)) (dolist (i (convert-to-base base int)) (incf ret i)) ret)) (dotimes (i 555) (if (zerop (mod 555 (sum-of-digits (+ 2 i) 555))) (format t „~d, “ (+ 2 i))))‘

dessen Ausgabe 2, 10, 11, 13, 16, 19, 21, 23, 37, 38, 46, 55, 61, 75, 91, 109, 111, 112, 136, 149, 181, 185, 186, 223, 260, 271, 276, 277, 371, 445, 519, 541, 551, 553 wohl alle Basen (außer denen größer gleich 555, für die das sowieso trivialerweise gilt) sein dürften, zu denen diese Zahl eine Harshad-Zahl ist.

Interessant.

17 Antworten zu Der fünfhundertfünfundfünfzigste Artikel

  1. Nur um dich ein Bisschen zu ärgern: Das entsprechende Haskell-Programm lautet:

    let crossfoot b = sum . map (`mod` b) . takeWhile (> 0) . iterate (`div` b) in filter (\b -> 555 `mod` crossfoot b 555 == 0) [2..555]

    Ausgabe:
    [2,10,11,13,16,19,21,23,37,38,46,55,61,75,91,109,111,112,136,149,181,185,186,223,260,271,276,277,371,445,519,541,551,553,555]

    Kannst du direkt in GHCi eingeben. =)

  2. dasuxullebt sagt:

    Also ich finde meinen Code selbst im unformatierten Zustand erheblich lesbarer. Das ist ungefähr das was ich auch Perl-Programmierern hin und wieder sage: Die Tatsache, dass man Code in sehr kurzer Weise aufschreiben kann, ist für mich kein Argument, dass eine Sprache gut ist. Haskell geht da vielleicht noch insofern einen guten Kompromiss ein, dass es mit Typsicherheit wenigstens sicherstellt, dass dass man nichts komplett falsches hinschreiben kann.

  3. Für mich ist hingegen dein Code ein einziges Rätsel. Klar, wenn ich ihn in eine Datei schreiben und richtig formatieren würde, würde ich auch ein Bisschen schlauer sein, aber das gilt für meinen Code ebenso. Dass du ihn nicht lesen kannst, ist reine Gewohnheit und die Tatsache, dass ich ihn so geschrieben habe, dass du ihn auf einer einzigen Zeile direkt in GHCi eingeben kannst. Hier nochmal derselbe Code, viel lesbarer:

    crossfoot :: Integral i => i -> i -> i
    crossfoot base =
      sum . map (`mod` base) .
      takeWhile (> 0) . iterate (`div` base)
    
    solutions :: Integral i => [i]
    solutions =
      filter
        (\base -> 555 `mod` crossfoot base 555 == 0)
        [2..555]

    Jetzt ist er sehr gut lesbar, und immer noch viel kürzer als dein Code. Und wenn du ihn jetzt nicht problemlos lesen kannst, dann behaupte nie wieder, dass du dich mit Haskell auch nur ansatzweise beschäftigt hast. ;)

  4. dasuxullebt sagt:

    Also mein Code in reinform

    (defun basednum-to-int (base digits)
      (if digits
        (+ (car digits)
           (* base (basednum-to-int base (cdr digits))))
        0))
    
    (defun basednum-inc (base digits)
      (if digits
        (if (< (car digits) (1- base))
          (cons (1+ (car digits)) (cdr digits))
          (cons 0 (basednum-inc base (cdr digits))))
        (list 1)))
    
    (defun convert-to-base (base int &optional (sofar nil))
      (if (equal int (basednum-to-int base sofar))
        sofar
        (convert-to-base base int (basednum-inc base sofar))))
    
    (defun sum-of-digits (base int)
      (let ((ret 0))
        (dolist (i (convert-to-base base int))
          (incf ret i))
        ret))
    (dotimes (i 555)
      (if (zerop (mod 555 (sum-of-digits (+ 2 i) 555)))
      (format t "~d, " (+ 2 i))))

    ist eigentlich schon recht gut lesbar. Vor Allem benutze ich keine Funktionen wie map, filter und keine punktfreie Notation, was den Code erheblich länger als unbedingt nötig macht, aber meiner Meinung nach eben auch übersichtlicher. Nicht dass er dadurch sinnvoller würde. Ging mir nur um einen schnellen Hack.

  5. Punktfreie Notation und der Verzicht auf Listenfunktionen sollen Code unübersichtlich machen? Stimmt, wenn du in Common Lisp denkst, stimmt aber nicht, wenn du in Haskell denkst. Die Art, in der ich den Code geschrieben habe, ist mit Abstand die natürlichste Art, ihn zu schreiben, die es gibt und sollte für jeden Haskell-Programmierer sofort klar sein, und das gilt sogar für den quick-n-dirty-Code von weiter oben. Du hast eine Funktionsdefinition zwischen ‚let‘ und ‚in‘ und hinter dem ‚in‘ benutzt du sie. Mehr ist das nicht.

  6. dasuxullebt sagt:

    Ich glaube, hier erreichen wir den Punkt wo es eindeutig Ansichtssache ist. Passend dazu vielleicht der Comic aus meinem früheren Blogpost: https://uxul.wordpress.com/2009/08/10/sichtweisen/

  7. Okay, und um zu demonstrieren, wie verschieden Lösungen auch bei gleicher Sprache sein können — meine quick’n’dirty-Lösung hätte wie folgt ausgesehen:

    (defun quersumme (n basis)
      (loop for (m digit) = (list n 0) then (multiple-value-list (truncate m basis))
            until (= 0 m digit)
            sum digit))
    
    (defun teilbar-durch-quersumme-p (n basis)
      (zerop (mod n (quersumme n basis))))
    
    (format t "~&~{~D~^, ~}"
            (loop for i from 2 to 554
                  when (teilbar-durch-quersumme-p 555 i)
                    collect i))

    Ich folgere daraus, daß man aus Codeschnipseln eh nicht unbedingt viel folgern kann. :)

  8. dasuxullebt sagt:

    Hmja. Sowas wie loop mag ich halt auch nicht, wenn es sich vermeiden lässt (und das lässt es sich in der Regel).

  9. Das hat mit Sichtweisen eigentlich wenig zu tun. Es gibt in jeder Programmiersprache eine ’natürliche Ausdrucksweise‘. Beispiel: Um die fünfzigste Fibonacci-Zahl zu finden, ist die natürliche Ausdrucksweise in Haskell, erst mal eine Liste _aller_ Fibonacci-Zahlen zu erstellen und aus dieser dann das fünfzigste Element herauszupicken:

    fibs :: Num a => [a]
    fibs = map fst . iterate (\(x,y) -> (y, x+y)) $ (0,1)

    main :: IO ()
    main = print (fibs !! 49)

    Das erscheint für einen Common Lisp-Programmierer natürlich merkwürdig, wenn nicht sogar irrsinnig, weil er sowas in seiner Sprache gar nicht machen würde (und wahrscheinlich auch nicht kann), ist es aber nicht. Common Lisp ist eben anders. Genau das Gleiche lässt sich auf den Code von weiter oben übertragen. Das ist nicht Ansichtssache, sondern Gewohnheitssache.

  10. dasuxullebt sagt:

    Coroutinen in Common Lisp sind leider nicht sehr weit verbreitet und entwickelt. Vermutlich kann man sie sich über Continuations (mittels cl/cont) irgendwie zusammenhacken. In Scheme gibt es sie aber bereits. Und Lazy Lists gibt es meines Wissens in Clojure, lazy lists wiederum ließen sich wohl in Common Lisp mit cl/cont herstellen (oder schlimmstenfalls per Threads). Was sehr schön ist in diesem Zusammenhang ist ContextL, da hat man ziemlich viele solche Objekt-Pushing-Mechanismen implementiert, ich habe es aber bisher nie benutzt, weil ich es bisher immer ziemlichen Overkill fand.

    Letztendlich sind „unendliche Listen“ nichts anderes als eine spezielle Form von Coroutinen, nur dass Haskell darauf optimiert ist. Andererseits ist das wieder ziemlich Funktional gedacht. Ich behaupte mal, die meisten „standard“-Programmierer werden damit Probleme haben.

    Fibonacci „natürlich“ würde man in haskell aber doch eher über die baumrekursive Definition a’la fib(0)=0, fib(1)=1, fib(n+2) = fib(n+1)+fib(n) definieren, oder? Nachdem diese in Haskell dank lazy evaluation nicht baumrekursiv ist. Das ist wieder eine der schönen Seiten von Haskell – man kann solche mathematischen Definitionen hinschreiben, und muss nicht auf den genauen Programmfluss achten.

  11. [L]azy lists wiederum ließen sich wohl in Common Lisp mit cl/cont herstellen (oder schlimmstenfalls per Threads).

    Ach, so schwere Geschütze braucht man da gar nicht aufzufahren. FORCE und DELAY sind leicht zu implementieren, dafür reichen schon FUNCALL und LAMBDA. Wenn man es higher-level haben will, kann man auch eine Bibliothek wie CLAZY verwenden.

    Man tut es eben nur reichlich selten — weil es nicht der CL-Kultur entspricht und sich aufgrund dessen auch nicht sehr gut mit Fremdcode verträgt.

    Lazy lists sind auch keine Coroutinen. Technisch mögen sie vergleichbar mit Generatoren sein und diese mit Coroutinen, aber paradigmatisch sind sie wohl kaum dasselbe.

    Die Denkweisen in Haskell und Lisp (wie über lazy lists und Coroutinen) sind einfach unterschiedlich. Nicht alles, was im einen natürlich ist, ist im anderen gleichermaßen natürlich oder auch nur übertragbar, und nur, weil etwas geht, ist es noch lange nicht angenehm oder guter Stil.

    Denkweisen sind auch genau das, worum es hier geht, und ich störe mich ein bißchen an dem Universalismus solcher Aussagen wie: „Das ist nicht Ansichtssache“.

    Das erscheint für einen Common Lisp-Programmierer natürlich merkwürdig, wenn nicht sogar irrsinnig …

    Die Lösung erscheint mir weder universell schön noch universell irrsinnig. Im Kontext von Haskell ist sie eine schöne Lösung. Im Kontext von Common Lisp würde ich sie in der Tat für „merkwürdig, wenn nicht sogar irrsinnig“ halten.

    Es ist eben doch alles abhängig vom Blickwinkel und in diesem Sinne eine Ansichtssache.

  12. Faule Verarbeitung bringt schon einige fundamentale Vorteile mit sich. Es ist beispielsweise sehr schwer, folgende Definitionen auf eine Sprache mit geiziger Verarbeitung zu übertragen:

    isPrime :: Integral i => i -> Bool
    isPrime n =
      all (\d -> rem n d /= 0) .
      takeWhile (\d -> d^2  [i]
    primes = 2 : filter isPrime [3..]

    Dieser Primzahlfilter funktioniert fast wie der naive Filter: einfach durch alle natürlichen Zahlen bis zur Quadratwurzel teilen. Allerdings teilt dieser Filter nur durch Primzahlen. Damit ist lazy evaluation nicht einfach nur eine andere Mundart, sondern tatsächlich eine sehr nützliche Spracheigenschaft.

    Auf der einen Seite erlaubt sie einem, bestimmte Dinge völlig natürlich, fast umgangssprachlich auszudrücken, die ersten zehn geraden natürlichen Zahlen etwa: take 10 . filter even $ [0..]. In anderen Sprachen muss man hier einen Startwert, die Schrittweite und den Endwert angeben — imperativ eben. In Haskell gibt man an, was „die ersten zehn geraden natürlichen Zahlen“ sind. Auf der anderen Seite kann man viele Algorithmen sehr elegant ausdrücken, wie den von weiter oben.

    (Die Zeilen 3 und 4 des Codes sind je mit zwei Spaces eingerückt.)

  13. dasuxullebt sagt:

    Das Konzept Lazy Evaluation ist eines von vielen sinnvollen Konzepten, die die Programmierwelt beeinflusst haben und beeinflussen. Sie ist ein nettes feature für einige Probleme – aber nicht für alle.

  14. (Das hier ist kein weiterer Beitrag. Der Code in meinem letzten ist immer noch falsch. Siehe . Ich habe die isPrime-Funktion allerdings auf zwei Zeilen verteilt.)

  15. dasuxullebt sagt:

    Sorry, ich weiß nicht was du meinst. Ich hab das was du gesendet hast einfach in pre-Tags. Die schlechten Format-Facilities von WordPress regen mich schon lange auf. Ich habe auch im Sinn sobald wie möglich davon wegzukommen. Allerdings – nunja, zu viel sonstiges zu tun bisher.

  16. Ich hatte nach dem „Siehe“ einen Link gepostet, aber wie es aussieht, wurde der entfernt. Hier nochmal:

    http://hpaste.org/fastcgi/hpaste.fcgi/view?id=16794

Schreibe einen Kommentar

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: