Grundlagen der funktionsorientierten Programmierung mit SCHEME/Von Fröschen und Tischlampen

Aus ProgrammingWiki

Wiki-Quelltext dieser Seite.

<language>SchemeJS</language>
<code id="5ba6aa64adda0" hidden>
(define stream-car car)

(define stream-cdr
  (lambda (stm)
    (force (cdr stm))))

(define-macro (stream-cons head tail)
  `(cons ,head (delay ,tail)))

(define stream?
  (lambda (stm)
    (and (pair? stm) (promise? (cdr stm)))))

(define stream-map
  (lambda (proz stm)
    (stream-cons (proz (stream-car stm)) (stream-map proz (stream-cdr stm)))))

(define show-stream
  (lambda (stm n)
    (letrec
        ([show
          (lambda (stm n ls)
            (if (= n 0)
                (apply display (append ls (list "...")))
                (show (stream-cdr stm) (- n 1) (append ls (list (stream-car stm) ", ")))))])
      (show stm n ()))))
</code>

[[Datei:FroschLampe.jpg|miniatur|Von Fröschen und Tischlampen]]

== Der Lampen-Stream ==

Zunächst definieren wir den Stream (abzählbar) unendlich vieler Tischlampen, die alle ausgeschaltet sind.<br />
Jede Lampe ist mit einem Schalter ausgestattet, mit dem die Lampe entsprechend ihres Betriebszustandes entweder ein- oder ausgeschaltet werden kann:

<code id="4b522b2b0bfdc">
(define lamp-stream
  (stream-cons 'off lamp-stream))

(define switch
  (lambda (lamp)
    (if (eqv? lamp 'off)
        'on
        'off)))
</code>

<run id="5beb53443aa6d">
(show-stream lamp-stream 20)
</run>

== Frosch-Sprünge mit verschiedenen Weiten ==

Um jede n-te Lampe schalten zu können, erweitern wir die Stream-Sprachelemente mit der Prozedur höherer Ordnung<tt>(stream-map-n <n>)</tt>.<br />
In Analogie zu <tt>stream-map</tt> bewirkt ein Aufruf von <tt>(stream-map-n <n>)</tt> die Anwendung einer Prozedur <tt>proz</tt> auf jedes <tt>n</tt>-te Element eines Streams <tt>stm</tt>:

<code id="5beb28d1485e5">
(define stream-map-n
  (lambda (n)
    (lambda (proz stm)
      (letrec
          ([help-stream
            (lambda (stm s)
              (if (= s n)
                  (stream-cons (proz (stream-car stm)) (help-stream (stream-cdr stm) 1))
                  (stream-cons (stream-car stm) (help-stream (stream-cdr stm) (+ s 1)))))])
        (help-stream stm 1)))))
</code> 

In folgendem Beispiel wird jede dritte Lampe eingeschaltet...
<run id="5beb28d148759">
(show-stream ((stream-map-n 3) switch lamp-stream) 20)
</run>

... bzw. ein- und wieder ausgeschaltet:
<run id="5c935079a5aa0">
(show-stream ((stream-map-n 3) switch ((stream-map-n 3) switch lamp-stream)) 20)
</run>

Nun können wir Frosch-Sprünge mit aufsteigenden Sprungweiten definieren:

<code id="5beb28d148663">
(define jump
  (lambda (w)
    (lambda (stm)
      (stream-cons 
       (switch (stream-car stm))
       ((jump (+ 1 w)) ((stream-map-n w) switch (stream-cdr stm)))))))
</code>

== Unendlich viele Frösche springen über unendlich viele Tischlampen ==

<code id="5beb28d1486d4">
(define frog-lamp-stream
  ((jump 1) lamp-stream))
</code>

<run id="4b52259a80ac2">
(show-stream frog-lamp-stream 20)
</run>

== Auswertung ==

Um das Resultat besser verallgemeinern zu können, fassen wir die Nummern der Lampen zusammen, die nach allen Frosch-Sprüngen ''eingeschaltet'' sind. In der Vermutung, dass das (abzählbar) unendlich viele Lampen sind, soll dieses Ergebnis wiederum als Stream repräsentiert werden:

<code id="4b52261eec295">
(define result-stream
  (lambda (stm)
    (letrec
        ([help-stream
          (lambda (stm i)
            (if (eqv? (stream-car stm) 'on)
                (stream-cons i (help-stream (stream-cdr stm) (+ i 1)))
                (help-stream (stream-cdr stm) (+ i 1))))])
      (help-stream stm 1))))
</code>

<run id="5ba6aa64ade24">
(show-stream (result-stream frog-lamp-stream) 15)
</run>

'''Ergebnis:''' Offensichtlich sind nach den Frosch-Sprüngen alle Lampen eingeschaltet, deren Nummern ''Quadratzahlen'' sind.<br />

Zurück zu [[Grundlagen der funktionsorientierten Programmierung mit SCHEME/Streams|Streams]].