Grundlagen der funktionsorientierten Programmierung mit SCHEME/Bedingte Ausdrücke

Aus ProgrammingWiki

Wiki-Quelltext dieser Seite.

<language>Scheme</language>
<code id="5071892b2a6b0" hidden>
;-- M. Hielscher / Chr. Wagenknecht / V. Berger --
(define random-maker
 (let* ((multiplier 48271)
        (modulus 21474836)
        (apply-congruence
         (lambda (current-seed)
           (let ((candidate (modulo (* current-seed multiplier) modulus)))
             (if (zero? candidate)
                 modulus
                 candidate))))
        (coerce
         (lambda (proposed-seed)
           (if (integer? proposed-seed)
               (- modulus (modulo proposed-seed modulus))
               19860617))))
 (lambda (initial-seed)
   (let ((seed (coerce initial-seed)))
     (lambda args
       (cond ((null? args)
              (set! seed (apply-congruence seed))
              (/ (- modulus seed) modulus))
             ((null? (cdr args))
              (let* ((proposed-top
                      (ceiling (abs (car args))))
                     (exact-top
                      (if (inexact? proposed-top)
                          (inexact->exact proposed-top)
                          proposed-top))
                     (top
                      (if (zero? exact-top)
                          1
                          exact-top)))
                (set! seed (apply-congruence seed))
                (inexact->exact (floor (* top (/ seed modulus))))))
             ((eq? (cadr args) 'reset)
              (set! seed (coerce (car args))))
             (else
              (display "random: unrecognized message")
              (newline))))))))

(require-library 'sisc/libs/srfi/srfi-19)
(import srfi-19)

(define random (random-maker (time-nanosecond (current-time))))

(define zufallszahl
  (lambda (a b)
    (+ a (random (- b a -1)))))

(define substring?
  (lambda (zk1 zk2)
    (cond
      [(> (string-length zk1) (string-length zk2)) #f]
      [(equal? zk1 (substring zk2 0 (string-length zk1))) #t]
      [else
       (substring? zk1 (substring zk2 1 (string-length zk2)))])))

(define klassenstaerke?
  (lambda (proz a b)
    (let* ([z (zufallszahl a b)] [test (apply proz (list z))])
      (cond
        [(void? test) #f]
        [(not (string? test)) #f]
        [(and (or (< z 10) (> z 32)) (substring? "unzul" (string-downcase test))) #t]
        [(and (< z 16) (substring? "gering" (string-downcase test))) #t]
        [(and (< z 21) (substring? "ideal" (string-downcase test))) #t]
        [(and (< z 26) (substring? "normal" (string-downcase test))) #t]
        [(and (< z 33) (substring? "ho" (string-downcase test))) #t]
        [else #f]))))
</code>

== Bedingte Ausdrücke in Scheme ==

Bedingte Ausdrücke sind Programmstrukturen, mit denen der ''Wert'' eines Ausdrucks von bestimmten ''Bedingungen'' abhängig gemacht werden kann.

<table border="1" cellspacing="1" cellpadding="4">
<tr>
<th width="37%">Grundform</th>
<th width="30%">Alternative</th>
<th width="33%">Fallauswahl</th>
</tr>
<tr>
<td>

<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td height="160" width="500" valign="top">
<code id="4c9cb9191269f">
(define gewichtspruefer
  (lambda (masse)
    (cond
      [(< masse 60) 'leichtgewicht]
      [(< masse 80) 'normalgewicht]
      [(< masse 100) 'schwergewicht]
      [else
       'uebergewicht])))
</code>
</td>
</tr>
<tr>
<td height="100" width="500" valign="top">
<run id="4c7ebb1941f9b">(gewichtspruefer 75)</run>
</td>
</tr>
</table>

</td>
<td>

<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td height="160" width="500" valign="top">
<code id="4c7ebb1941871">
(define my-abs
  (lambda (x)
    (if (< x 0)
        (- x)
        x)))
</code>
</td>
</tr>
<tr>
<td height="100" width="500" valign="top">
<run id="4c7ebb19420c5">(my-abs -3.45)</run>
</td>
</tr>
</table>

</td>
<td>

<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td height="160" width="500" valign="top">
<code id="4c9e20fdaaf21">
(define vokal?
  (lambda (zeichen)
    (case zeichen
      [(#\a #\e #\i #\o #\u) #t]
      [(#\A #\E #\I #\O #\U) #t]
      [else #f])))
</code>
</td>
</tr>
<tr>
<td height="100" width="500" valign="top">
<run id="4c9e20fdab08f">(vokal? #\o)</run>
</td>
</tr>
</table>

</td>
</tr>
<tr>
<td valign="top">
'''Beachte:'''<br />
<ul>
<li>die angegebenen Bedingungen werden in der vorgegebenen Reihenfolge evaluiert,</li>
<li>sobald sich zum ersten Mal der Wahrheitswert <tt>#t</tt> ergibt, werden keine weiteren Bedingungen untersucht
(selbst dann nicht, wenn ein fehlerhafter Ausdruck wie <tt>(/&nbsp;4&nbsp;0)</tt> erscheint) – die Evaluation wird an dieser Stelle beendet,</li>
<li>else wird immer zu <tt>#t</tt> evaluiert, sodass bei seiner Evaluation immer der nachfolgende Ausdruck abgearbeitet wird.</li>
</ul>
</td>
<td valign="top">
'''Beachte:'''<br />
<ul>
<li>der <tt>else</tt>–Teil kann grundsätzlich entfallen,</li>
<li>in beiden Teilen darf nur ein Ausdruck stehen, anderenfalls muss mit
<tt>(begin&nbsp;<Ausdruck>&nbsp;...&nbsp;<Ausdruck>)</tt> ein ''zusammengesetzter Ausdruck'' gebildet werden,</li>
<li>unbestimmte Rückgabewerte, wie sie bei
<pre>
(define my-sqrt
  (lambda (x)
    (if (>= x 0) (sqrt x))))
</pre> 
auftreten können, sollten vermieden werden.</li>
</ul>
</td>
<td valign="top">
'''Beachte:'''<br />
<ul>
<li>zunächst wird der nach <tt>case</tt> folgende Ausdruck evaluiert,</li>
<li>in einer Suche von oben nach unten wird festgestellt, in welcher Auswahlmenge sein Wert vorkommt,</li>
<li>der zu dieser Menge gehörende Ausdruck wird evaluiert und als Wert des gesamten <tt>case</tt>–Ausdrucks zurückgegeben,</li>
<li>falls der Wert in keiner Menge enthalten ist, bildet der Wert nach <tt>else</tt> das Resultat.</li>
</ul>
</td>
</tr>
</table>

== Aufgaben ==

<ol>
<li>
=== Signumfunktion ===

Schreiben Sie eine Prozedur für die einstellige Funktion <tt>signum</tt>, die als Funktionswerte
-1, 1 oder 0 entsprechend des Vorzeichens der als Argument übergebenen Zahl zurückgibt.<br />
'''Beispiele:'''
<pre>
> (signum -337.8436) --> -1
> (signum 0)         -->  0
> (signum 34/45)     -->  1
</pre>

<code id="4c7ebb1941ac3">
(define signum
  (lambda (x)
    ...))
</code>

<check>
(and (= (signum -337.8436) -1) (= (signum 0) 0) (= (signum 34/45) 1))
</check>

<run id="4c7ebb1942316">
(signum -337.8436)
</run>
</li>
<li>
=== Palindromprüfer ===

Entwickeln Sie das Prädikat <tt>palindrom?</tt>, das zu einer übergebenen Zeichenkette entscheidet, ob sie vor- und rückwärts gelesen gleich lautet.<br />
'''Beispiele:'''
<pre>
> (palindrom? "informatik") --> #f
> (palindrom? "otto")       --> #t
</pre>

'''Hinweis:''' Sie können zur Lösung folgende Sprachelemente nutzen:

<pre>
> (equal? "otto" "otto") --> #t
> (reverse '(a b c d e)  --> (e d c b a)
</pre>

<code id="525485b6ecde6">
(define palindrom?
  (lambda (zk)
    ...))
</code>

<check>
(and (palindrom? "einnegermitgazellezagtimregennie") (palindrom? "rentner") (not (palindrom? "informatik")))
</check>

<run id="525485b6eceec">
(palindrom? "otto")
</run>
</li>
<li>
=== Klassenstärkenprüfer ===

Lehrer und Schüler bewerten Klassenstärken etwa mit folgenden Einschätzungen:
* 10 ... 15: geringe Klassenstärke,
* 16 ... 20: ideale Klassenstärke,
* 21 ... 25: normale Klassenstärke,
* 26 ... 32: hohe Klassenstärke,
Alle anderen Schülerzahlen sind unzulässig.
Implementieren Sie einen "Klassenstärkenprüfer" als bedingten Ausdruck mit den Sprachelementen:
* <tt>cond</tt>
* <tt>if</tt>
* <tt>case</tt>.
Die Prozeduren sollen das Bewertungsergebnis als Zeichenkette zurückgeben.

<code id="4c7ebb1941bef">
(define klassenstaerke1
  (lambda (anzahl)
    (cond
      ...)))
</code>

<check>
(and (klassenstaerke? klassenstaerke1 1 9)
     (klassenstaerke? klassenstaerke1 10 15)
     (klassenstaerke? klassenstaerke1 16 20)
     (klassenstaerke? klassenstaerke1 21 25)
     (klassenstaerke? klassenstaerke1 26 32)
     (klassenstaerke? klassenstaerke1 33 100))
</check>

<run id="4c7ebb194248a">
(klassenstaerke1 12)
</run>

<code id="4c7ebb1941d1d">
(define klassenstaerke2
  (lambda (anzahl)
    (if
      ...)))
</code>

<check>
(and (klassenstaerke? klassenstaerke2 1 9)
     (klassenstaerke? klassenstaerke2 10 15)
     (klassenstaerke? klassenstaerke2 16 20)
     (klassenstaerke? klassenstaerke2 21 25)
     (klassenstaerke? klassenstaerke2 26 32)
     (klassenstaerke? klassenstaerke2 33 100))
</check>

<run id="4c7ebb19425b3">
(klassenstaerke2 21)
</run>

<code id="4c7ebb1941e4e">
(define klassenstaerke3
  (lambda (anzahl)
    (case
      ...)))
</code>

<check>
(and (klassenstaerke? klassenstaerke3 1 9)
     (klassenstaerke? klassenstaerke3 10 15)
     (klassenstaerke? klassenstaerke3 16 20)
     (klassenstaerke? klassenstaerke3 21 25)
     (klassenstaerke? klassenstaerke3 26 32)
     (klassenstaerke? klassenstaerke3 33 100))
</check>

<run id="4c7ebb19426de">
(klassenstaerke3 38)
</run>
</li>
<li>
=== Konstruierbarkeit von Dreiecken ===

<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td valign="top">
Zu drei beliebig gegebenen Seitenlängen kann nicht in jedem Fall ein Dreieck konstruiert werden (vgl. Abbildung).<br />
Vervollständigen Sie das Prädikat <tt>konstruierbar?</tt>, mit dem geprüft werden soll, ob ein Dreieck zu den gegebenen Seitenlängen <tt>a</tt>, <tt>b</tt> und <tt>c</tt> konstruierbar ist.<br />
Beachten Sie dabei, dass diese Seitenlängen in einer beliebigen Reihenfolge gegeben sein können.

'''Beispiele:'''
<pre>
> (konstruierbar? 3 4 5) --> #t
> (konstruierbar? 2 7 2) --> #f
</pre>
</td>
<td  valign="top">
[[Bild:Dreieckskonstruktion1.gif|miniatur|hochkant=2|Zur Konstruierbarkeit von Dreiecken]]
</td>
</tr>
</table>

'''Hinweis:''' Sie können zur Lösung die variabelstellige Prozedur <tt>max</tt> nutzen:

<pre>
> (max 3 4 5 6 7 5 7 2) --> 7
</pre>

<code id="507197059a4a1">
(define konstruierbar?
  (lambda (a b c)
    ...))
</code>

<check>
(and (konstruierbar? 3 4 5) (not (konstruierbar? 2 7 2)) (not (konstruierbar? 9 3 6)))
</check>

<run id="507197059a587">
(konstruierbar? 3 4 5)
</run>
</li>
</ol>