Posts Tagged ‘Lisp’

Berechnen der nten Wurzel (Python, Scheme)

Tuesday, June 17th, 2008

Einmal von Python, um meinen alten Beitrag fuer Quadratwurzeln zu uebertrumpfen, und zum anderem per Scheme, um einfach die unglaubliche Ausdrucksfaehigkeit der Sprachen der LISP-Familie zum Ausdruck kommt.

Man koennte, natuerlich auch die mathematische Grundlagen von Python/Scheme ausnutzen und n**1/root (bzw. (expt n 1/root)) rechnen lassen und somit sich gar nicht um die Implementierung eines Verfahrens zur Berechnung einer Wurzel kuemmern; allerdings kann man davon nichts lernen.

Python:

from decimal import Decimal

def callRecNthroot(num=1, approx=1, root=2, steps=5):
    if approx == 1:
        approx /= root
    num, approx = Decimal(str(num)), Decimal(str(approx))
    return recNthroot(num, approx, root, steps)

def recNthroot(num, approx, root, steps)
    if steps > 0
        return recNthroot(num, (1/root)*((root-1)*approx + (num/(approx**(root-1))), root, (steps-1))
    return approx

Eigentlich ganz verstaendlich, bis auf ein paar Einfluesse von mir und meinem eher funktionalen Hintergrund, der auch Faulheit enthaelt.

foo /= n

bedeutet

foo = foo/n

. Meinen funktionalen (Wtf? `funktionen’? War ich da besoffen?) Hintergrund sieht man bei der tail-recursion: Ich packe alle Veraenderungen in den erneuten Funktionsaufruf, auch wenn das recht nervig ist, wenn man das aus dem Kopf heraus macht. Der neue Wert von approx stimmt; das ist das Newtonverfahren, wenn man nicht ableiten kann bzw. gerade keine Ableitfunktion (ist sehr kurz) schreiben moechte. Ich habe in Python einfach mal das `steps’-Argument benutzt, anstatt eine Praezision zu definieren; solange man eine nicht allzu ferne Naeherung angibt, so sind i.d.R. alle Stellen bis steps^2 korrekt.

Scheme:

(define (enough-prec? a b prec)
        (< (abs (- a b)) prec))

(define (nthroot n root)
        (lambda (x)
                (cond
                        ((enough-prec? n (expt x root) 0.0001) x)
                        (else ((nthroot n root) (* (/ 1 root) (+ (* (- root 1) x) (/ n (expt x (- root 1))))))))))

(Der letzte Teil ist so ziemlich das Gleiche.)

Man ruft die Funktion per ((nthroot n root) <approximation>) (Vergessen, dass HTML die Kleiner- und Groeßerzeichen frisst.) auf; fuer die Leute, die kein Scheme koennen. Die Annaeherung sollte moeglichst eine Dezimalzahl sein, da Scheme ansonsten ganz stolz eine rationale Zahl hervorbringt, die u.U. lang und unverstaendlich ist. (Probiert etwa mal ((nthroot 1000 3) 15); zwar bekommt man schnell etwas zurueck, allerdings hat das bei mir in Guile fast 2 Zeilen verschlungen.) Nun aber zum Code: Die Funktion (enough-prec?) nimmt 3 Argumente; zwei Zahlen, die sie voneinander subtrahieren soll und danach den Betrag davon berechnen soll, und eine Praezision; recht trivial, noch. Jetzt kommen wir zur Hauptfunktion: Ich benutze (lambda), um eine anonyme Funktion zu benutzen, die recht nett und praktisch sind. In ihr benutze ich (cond), um zwei Faelle zu unterscheiden: Zum einem, ob genuegend Praezision erlangt wurde; falls dies der Fall sein sollte, so spuckt er meine Annaeherung aus. Wenn nicht, wird er (nthroot) mit selbem n und root, aber einem neuen Argument fuer (lambda), aufrufen; eine tail-recursion; oder eher mies uebersetzt: Endrekursion. Der Trick dabei: Durch (enough-prec?) wird er solange weiterrechnen, bis man die gewollte Praezision hat; hier ist es 0.0001.

Was wohl eher auf einem Taschenrechner landet? Letztere Methode, mit so viel Genauigkeit, wie der Rechner Stellen anzeigt. Durch einen Tastendruck wird wohl eine Abstraktionsfunktion, sagen wir mal (nroot) aufgerufen, die n und root benoetigt und daraus eine sinnvolle Approximation macht. Wenn der Wert zu hoch ist, etwa ((nthroot 8 3) 64), so koennte er ewig weiterrrechnen. (Die Naeherungsmethode wuerde so eine immer groeßer werdende Naeherung herausgeben.)

(so long and (more (fun understanding)))

Python und Scheme

Thursday, June 5th, 2008

Ich weiß ja auch nicht, warum Lisp kein Industriestandard wurde; allerdings ist folgendes laecherlich:

In Scheme kann ich die “accumulate”-Funktion aus dem SICP ohne Probleme implementieren. In Python habe ich mit `eval’ gerungen und muss jedes Argument als String angeben. Sprachen, die kein `eval’ haben, werden wohl gar keine solche Funktion definieren koennen und somit kaum abstrakt arbeiten koennen. Schlimm.

Python: http://pastecode.com/?show=m3128cab7
Scheme: http://pastecode.com/?show=mc4e5c48

Und nun sag mal jemand, dass sich Lisp nicht lohne.

So long.

Programmiersprachen

Tuesday, June 3rd, 2008

Neben meines Schreibanfalls wegen dieser verdammten Maus, habe ich mir heute mal wieder ein paar Gedanken zum Thema Programmiersprachen gemacht; vor allem, da ich ja Lisp (derzeitig am ehesten Scheme durch das SICP) lerne und es - imo - eine Ausdrucksfaehigkeit hat, die von keiner anderen Sprache angegriffen werden kann. Allerdings: Wenn man allein durch die Ausdrucksfaehigkeit seine Sprache waehlt, so programmiert man in einem Vakuum; man laesst den Luftwiderstand - oder, um es passender zu formulieren, den “Ich-entwerfe-noch-einmal-das-Rad-und-zwar-doppelt-so-gut”-Faktor - weg.

Was ist denn dieser komische Faktor? Einfach: In Python (oder ein Java, wer diese gehypte Sprache mag) hat man schon eine Menge von Libraries, die einem vieles abnehmen. Du musst HTML parsen? Kein Problem - Python hat dafuer schon ‘ne Lib - `Modul’ auf Pythonisch. In Scheme sucht man vergeblich nach so ‘ner Lib - es soll ja eine abgespeckte Version von Common Lisp, der gigantischen Multiparadigmapsprache, die mehr Augen hat, als Java Libs, sein. (Das Standard von Scheme passt eigentlich fast gar in den Index des Standards von CL; da erkennt man den unglaublichen Unterschied zwischen diesen beiden Dialekten von Lisp.)

Wenn man sich traut, sich diese Libs selbst zu schreiben - und nebenbei produktiv zu sein, d.h. etwas anderes machen als die Libs zu schreiben - dann soll man es ruhig tun. Klar - es ist ein gigantischer Gewinn, wenn’s um Faehigkeit geht, aus dem geringem, welchem man in Scheme hat, solche Libs zu bauen - allerdings hat der schlechte Javaprogrammierer, waehrend der Schemeprogrammierer unglaublich produktiv seine Libs macht, schon laengst seinen Auftrag erledigt. CL hat nicht allzu viel von diesem Defizit; dafuer wollen aber viele schon wegen den Sexps (“S-expressions”, ihr notgeilen Deppen!) und der komischen Makrosyntax gar nichts davon wissen - obwohl beide eben den LISP-Programmierer so unglaublich maechtig machen.

Insofern kann man sagen, dass CL - auch wenn es so gigantisch ist und, nebenbei bemerkt, ein veraltetes Librarysystem hat - durchaus eine Alternative zu Java ist. Bloß warum wird CL nicht oefters benutzt? Weil Java einfach nur richtig dick von Sun gehypt wurde. CL ist schneller, besser und ausdrucksfaehiger als Java - und je mehr Personen eine Sprache nutzen, desto mehr Libs wird es dafuer geben. (Nebenbei bemerkt: Manche Compiler von Scheme schaffen - wirklich - eine Geschwindigkeit von Code, die die Herrschaft von C im Bereich der Geschwindigkeit angreift - und diese Compiler sind selbst in Scheme geschrieben.)