Lisp



  • Hallo zusammen
    Ich schreibe gerade eine kleine Arbeit über Lisp und suche in diesem Zusammenhang möglichst viele kleine Programmkonstrukte, welche in C nicht oder nur mit sehr mühsamer Syntax nachgebildet werden können:

    (defun addn (n) #'(lambda (x) (+ x n)))

    Dieser Code kann bspw. IMHO in C nicht nachgebildet werden, und genau solche Beispiele suche ich 😉

    Was ich zusätzlich verzweifelt suche ist eine Programmieraufgabe, die ich mit Lisp deutlich einfacher lösen kann als mit C.

    Ich bin Dankbar für eure Inputs 🙂

    Mfg Samuel



  • Ich hab nur sehr sehr geringe Lisp Kenntnisse, aber das was mich am meisten beeindruckt hat, war das Macro System.
    Da Anweisungen in Lisp selbst wieder ganz normale Listen sind, sind Macros anders als in C keine Textersetzung, sondern operieren wie ganz ganz normale List Funktionen (nur eben zur Compile-Zeit) auf Listen und geben welche zurück.

    Ich denk mal, da lässt sich bestimmt ein gutes Beispiel finden.



  • Ishildur schrieb:

    (defun addn (n) #'(lambda (x) (+ x n)))

    Dieser Code kann bspw. IMHO in C nicht nachgebildet werden...

    erzähl doch mal, was der macht.
    🙂



  • welche in C nicht oder nur mit sehr mühsamer Syntax nachgebildet werden können

    Prinzipiell ist alles auch in C moeglich, aber ungemein aufwendig. Beispiele fuer lisptypische (ich komme eher aus der Schemewelt) Sachen sind: Closures, continuations, Funktionen hoeherer Ordnung.

    erzähl doch mal, was der macht

    Gibt 'ne Funktion zuerueck, die n auf x addiert. Verwandt mit Currying/Schoenfinkeln. Wobei das #' irgendwie verwirrt.



  • @fricky
    In C (und auch allen anderen imperativen Sprachen) kann man Funktionen definieren und mit hilfe von Parametern parametrisieren. Bei Lisp ist es nun zusätzlich möglich, den Funktionsbody selbst dynamisch zu machen, was eben bei C nicht möglich ist...

    In C:

    int add1(int x){
     return x+1;
    }
    
    int add2(int x){
     return x+2;
    }
    
    int add3(int x){
     return x+3;
    }
    
    int (*addn(int n))(int){
     switch(n){
      case 1: return &add1; break;
      case 2: return &add2; break;
      case 3: return &add3; break;
     }
    }
    

    In Lisp:

    (defun addn (n) #'(lambda (x) (+ x n)))
    

    Man kann nun deutlich sehen, dass C einerseits extrem viel aufwändiger ist und eben nicht parametrisierbar ist, weil einfach alles statisch definiert werden muss. Was, wenn von add1 bis add10000 alles möglich sein muss? Viel Spass beim tippen (Die resultierende Programmgrösse dürfte dann auch nicht ohne sein :p )



  • @knivil
    Das #' dient dazu, dass ein symbolischer Name für die Funktion zurückgegeben wird. Ohne das #' würde die Funktion evaluiert und in einem Kompilerfehler enden, weil der Parameter x fehlt...



  • EDIT: hier stand Bloedsinn.

    Ishildur schrieb:

    @fricky
    In C (und auch allen anderen imperativen Sprachen) kann man Funktionen definieren und mit hilfe von Parametern parametrisieren. Bei Lisp ist es nun zusätzlich möglich, den Funktionsbody selbst dynamisch zu machen, was eben bei C nicht möglich ist...

    In C++ ist das Templates oder mit lambda und bind auch ein Einzeiler. Selbst in C koennte man Funktionen hoeherer Ordnung halbwegs brauchbar auf structs abbilden. Wenn du echte lispische Sachen suchst, wuerd ich auch eher in Richtung Makros gehn.



  • @Blue-Tiger
    Kannst du das bitte mit Templates schreiben? Ich kanns mir gerade nicht vorstellen, was aber natürlich nicht heissen soll, dass es nicht möglich wäre! 😉



  • Ishildur schrieb:

    @Blue-Tiger
    Kannst du das bitte mit Templates schreiben? Ich kanns mir gerade nicht vorstellen, was aber natürlich nicht heissen soll, dass es nicht möglich wäre! 😉

    hmm.... mit templates wohl doch nur zur Compilezeit. Aber mit bind schaff ich's auch zur Laufzeit.



  • int (*addn(int n))(int)
    

    Da muss die Signatur aber vorher festgelegt sein.

    Selbst in C koennte man Funktionen hoeherer Ordnung halbwegs brauchbar auf structs abbilden.

    Ja und nein, da die Signatur der Funktion vorher immer bekannt sein muss. Das Lambda im neuen C++ Standard ist eingeschraenkter, als dass in Scheme, Lisp oder Haskell.

    Auch weiss ich nicht, warum der symbolische Name so interessant ist. In Scheme wuerde man das Resultat einfach in einer Variablen speichern und dann benutzen. Es ist ein Beispiel fuer Closures. In Scheme wuerde man es wie folgt ausdruecken:

    (define (addn n) (lambda (x) (+ x n))) ;; define closure "constructor"
    (define add5 (addn 5)) ;; create closure "object" with internal state 5
    (define result1 (add5 4)) ;; use closure, result1 is 9
    (define result2 (add5 5)) ;; use closure, result2 is 10
    
    (define add6 (addn 6)) ;; create another closure "object" with internal state 6
    (define result3 (add6 4)) ;; use closure, result3 is 10
    


  • Ishildur, es gibt von Paul Graham "On Lisp" irgendwo im Netz. Blätter das mal durch, da wirst du einiges finden ...

    Ishildur schrieb:

    Das #' dient dazu, dass ein symbolischer Name für die Funktion zurückgegeben wird. Ohne das #' würde die Funktion evaluiert und in einem Kompilerfehler enden, weil der Parameter x fehlt...

    Nein, ohne das #' steht da

    (defun addn (n)
      (lambda (x) (+ x n)))
    

    Was jetzt passiert, hängt davon ab, ob es eine Funktion LAMBDA gibt: Wenn ja, werden (x) und (+ x n) evaluiert. Letzteres sollte kein Problem sein, das erste greift auf eine (vom Formalparameter x unabhängige!) Funktion X zu, die es entweder gibt oder nicht.
    Zum Glück ist LAMBDA aber ein Makro, das nichts anderes tut, als sich in #'(LAMBDA ...) zu transfomieren 😉



  • knivil schrieb:

    int (*addn(int n))(int)
    

    Da muss die Signatur aber vorher festgelegt sein.

    Selbst in C koennte man Funktionen hoeherer Ordnung halbwegs brauchbar auf structs abbilden.

    Ja und nein, da die Signatur der Funktion vorher immer bekannt sein muss. Das Lambda im neuen C++ Standard ist eingeschraenkter, als dass in Scheme, Lisp oder Haskell.

    Du kannst die Signatur mit void*-Pointern allgemein genug halten 😉



  • Blue-Tiger schrieb:

    Du kannst die Signatur mit void*-Pointern allgemein genug halten 😉

    Ja, auch die Anzahl kann man variable halten. Aber trotzdem kriegt man Closures nicht ohne weiteres hin. Auch sind Funktionszeiger nicht wirklich damit vergleichbar, da ich innerhalb der Funktion *int (addn(int n))(int) keinen Zustand gespeichert werden kann (globale Variablen sind keine Loesung).

    Ansonsten kannst du gerne diese Zeile in C nachbauen:

    (define (addn n) (lambda (x) (+ x n)))
    

    Ich moechte sie genauso oder aehnlich wie im geposteten Schemecode benutzen. Und dann zaehlen wir mal Codezeilen ... Das Hauptproblem ist, dass Funktionen in C keine first class values sind. Closures kann man durch Objekte simulieren, aber braucht natuerlich auch mehr Codezeile.



  • Deswegen sagte ich ja, dass man Funktionen auf structs abbilden muesste. die koennten zusaetzlichen Status speichern. Allerdings braeuchte es vermutlich einiges an Code und Gehirnschmalz, bis da ein halbwegs brauchbares Framework entsteht, aber es ist fern von "unmoeglich" (schon allein wegen Greenspun's 10th law 😉 ).



  • aber es ist fern von "unmoeglich"

    Selbstzitat (3te Antwort):

    Prinzipiell ist alles auch in C moeglich, aber ungemein aufwendig.



  • Ishildur schrieb:

    Ich schreibe gerade eine kleine Arbeit über Lisp und suche in diesem Zusammenhang möglichst viele kleine Programmkonstrukte, welche in C nicht oder nur mit sehr mühsamer Syntax nachgebildet werden können

    Das meiste wird auch mit anderen Sprachen als den Lisp's gehen. Was nur in Lisp geht, ist zB sowas:

    ; Common Lisp (ungetestet)
    
    (defmacro increase (x)
      `(setq ,x (+ ,x 1)))
    

    Dafür müsste man in Sprachen mit Syntax noch eine Struktur für den Syntax-Baum festlegen. Die gleiche Beobachtung erspart dem metalinguistischen Interpreter aus SICP den Parser.
    Alles andere sollte auch mit anderen Sprachen machbar sein.

    fricky schrieb:

    erzähl doch mal, was der macht.

    Das ist das gleiche wie zB in Javascript

    function addn (n)
    {
        return function (x) { return x + n; };
    }
    

    Das seltsame # ist eine Besonderheit von Common Lisp, das ist prinzipiell nicht nötig. Common Lisp ist irgendwie ein wenig wie C++.
    🙂





  • Bashar schrieb:

    Ishildur, es gibt von Paul Graham "On Lisp" irgendwo im Netz. Blätter das mal durch, da wirst du einiges finden ...)

    0x06. Dort wirst du sicher genug für eine kleine Arbeit finden:
    http://www.paulgraham.com/onlisp.html

    Zu Continuations willst du vielleicht auch das Kapitel über Web Applications in PLAI lesen:
    http://www.cs.brown.edu/~sk/Publications/Books/ProgLangs/2007-04-26/

    Wie man sowas in CL nachbaut, steht wiederum bei Graham.
    🙂



  • knivil schrieb:

    Aber trotzdem kriegt man Closures nicht ohne weiteres hin.

    Sollte schon recht einfach gehen. Closures kann man als Verschachtelung von assioziativen Arrays beschreiben (oder durch name mangling, wenn du's hart übersetzen willst). Continuations bekommst du dann als Verschachtelung von Closures.
    🙂



  • Closures kann man als Verschachtelung von assioziativen Arrays beschreiben

    Ich verstehe nicht so recht, was deine Idee ist. Aber vom Array ist es nun gar nicht weit zur List und wir waeren bei Lisp. Und was das bedeutet, hat Blue-Tiger ja schon gesagt: Greenspun's 10th law. Klar geht alles, aber muss man dafuer unbedingt Lisp nachbauen? Anscheinend schon.


Anmelden zum Antworten