summaryrefslogtreecommitdiff
path: root/doc/user-manual/1.7.3-pd/doc/pd.Handbuch.Teil6b
blob: 7fdaf3969873569e001022e40623e9ca450a307a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
                     EUMEL-Benutzerhandbuch 
 
 
                     TEIL 6: Erste Hilfe in ELAN                     
 
Prozeduren 
 
Prozeduren werden verwendet, wenn 
 
- Anweisungen und Datenobjekte unter einem Namen zusammengefaßt werden 
  sollen ("Abstraktion"). 
 
- gleiche Anweisungen von mehreren Stellen eines Programms verwandt werden 
  sollen (Codereduktion) u.U. mit verschieden Datenobjekten (Parameter); 
 
- wenn Datenobjekte nur kurzfristig benötigt werden (dynamische Speicherver- 
  waltung) und diese nicht von dem gesamten Programm angesprochen werden 
  sollen (lokale, globale Datenobjekte); 
 
Im folgenden Programm stellen wir ein Fragment vor, in dem zwei Werte ver- 
tauscht werden. In der einen (linken) Lösung wird ein Refinement, in der 
anderen eine Prozedur verwandt. 
 
Programm 19: 
 
     ...                            PROC vertausche a und b: 
     IF a > b                          INT CONST x :: a; 
       THEN vertausche a und b         a := b; 
     END IF;                           b := x 
     ...                            END PROC vertausche a und b; 
     vertausche a und b;            ... 
     ...                            IF a > b 
                                      THEN vertausche a und b 
     vertausche a und b:            END IF; 
        INT CONST x :: a;           ... 
        a := b;                     vertausche a und b; 
        b := x.                     ... 
 
Beim ersten Hinsehen leisten beide Programme das Gleiche. Es gibt jedoch 
drei wichtige Unterschiede: 
 
1) Das Refinement 'vertausche a und b' wird zweimal (vom ELAN-Compiler) 
   eingesetzt, d.h. der Code ist zweimal vorhanden. Die Prozedur dagegen ist 
   vom Code nur einmal vorhanden, wird aber zweimal - durch das Aufführen 
   des Prozedurnamens - aufgerufen. 
 
2) Die Variable 'x' ist in der linken Programmversion während des gesamten 
   Ablauf des Programms vorhanden. Solche Datenobjekte nennt man statische 
   Datenobjekte oder auch (aus Gründen, die erst etwas später offensichtlich 
   werden) Paket-Objekte. Das Datenobjekt 'x' der rechten Version dagegen 
   ist nur während der Bearbeitung der Prozedur vorhanden. Solche Daten- 
   objekte, die nur kurzfristig Speicher belegen, werden dynamische Daten- 
   objekte genannt. 
 
   Prozeduren sind also ein Mittel, um die Speicherbelegung zu beeinflussen, 
   was besonders bei großen Datenobjekten notwendig ist. 
 
3) 'x' kann in der linken Programmversion - obwohl sie in einem Refinement 
   deklariert wurde - von jeder Stelle des Programms angesprochen werden. 
   Solche Datenobjekte werden globale Datenobjekte genannt. Das Datenobjekt 
   'x' der Prozedur dagegen kann nur innerhalb der Prozedur angesprochen 
   werden, sie ist also ein lokales Datenobjekt hinsichtlich der Prozedur. 
   Innerhalb der Prozedur dürfen globale Datenobjekte (also Objekte, die 
   außerhalb von Prozeduren deklariert wurden) angesprochen werden. 
 
   Eine Prozedur in ELAN bildet im Gegensatz zu Refinements einen eigenen 
   Gültigkeitsbereich hinsichtlich Datenobjekten und Refinements, die inner- 
   halb der Prozedur deklariert werden. Prozeduren sind somit ein Mittel, um 
   die in ihr deklarierten Datenobjekte hinsichtlich der Ansprechbarkeit 
   nach Außen "abzuschotten". 
 
Prozeduren wie die in Programm 19 werden mit dem Schlüsselwort PROC, dem 
Namen der Prozedur und einem Doppelpunkt eingeleitet. Dies nennt man den 
Prozedurkopf. Der Prozedurkopf entspricht den Definitionen, die wir bereits 
in vorigen Abschnitten dieses Skripts gegeben haben. Nach dem Prozedurkopf 
folgt der Prozedurrumpf, der Datenobjekt-Deklarationen, Anweisungen und 
Refinements enthalten kann. Abgeschlossen wird die Prozedur durch END PROC 
und dem Prozedurnamen. 
 
 
Aufgabe (HSG): 
 
     a) Erkläre den Unterschied zwischen einer Prozedur und einem Refinement. 
     b) Was sind globale bzw. lokale Datenobjekte? 
Übungsziel: Begriffe, die mit Prozeduren zusammenhängen 
 
 
Aufgabe (TSW): 
 
     Gegeben sei folgendes Programmfragment: 
        INT VAR a, b; 
        ... (*1*) 
        PROC x: 
           INT VAR x1; 
           ... (*2*) 
        END PROC x; 
        TEXT VAR t; 
        PROC y: 
           REAL VAR y1 
           ... (*3*) 
        END PROC y; 
        ... (*4*) 
 
     1.) Welche Objekte (einschließlich Prozeduren) sind an den Punkten 
           (*1*) : 
           (*2*) : 
           (*3*) : 
           (*4*) : 
         ansprechbar? 
 
     2.) Welche Datenobjekte sind in der Prozedur 'y' 
         global: 
         lokal: 
Übungsziel: Globale und lokale Objekte 
 
 
Prozeduren mit Parametern erlauben es, gleiche Anweisungen mit unterschied- 
lichen Datenobjekten auszuführen: 
 
 
Programm 20: 
 
     PROC vertausche (INT VAR a, b): 
        INT VAR x :: a; 
        a := b; 
        b := x 
     END PROC vertausche; 
 
     INT VAR eins :: 1, 
             zwei :: 2, 
             drei :: 3; 
     vertausche (eins, zwei); 
     vertausche (zwei, drei); 
     vertausche (eins, zwei); 
     put (eins); put (zwei); put (drei) 
 
Die Datenobjekte 'a' und 'b' der Prozedur 'vertausche' werden formale Para- 
meter genannt. Sie stehen als Platzhalter für die bei einem Prozeduraufruf 
einzusetzenden aktuellen Parameter (in unserem Beispiel die Datenobjekte 
'eins', 'zwei' und 'drei'). 
 
 
Aufgabe (HSG): 
 
     Welche Werte werden in dem Programm 20 ausgedruckt? 
Übungsziel: Arbeiten mit Prozeduren und Parameter-Mechanismus. 
 
 
Parameter werden im Prozedurkopf nach dem Prozedurnamen in Klammern mit 
Datentyp und Accessrecht angegeben. Dabei bedeutet CONST, daß auf den 
Parameter nur lesend zugegriffen wird, während auf einen VAR-Parameter auch 
z.B. eine Zuweisung angewandt werden kann. CONST-Parameter sind also 
Eingabe-Parameter, während VAR-Parameter Ein-/Ausgabe-Parameter realisieren. 
 
Bei den aktuellen Parametern ist folgendes zu beachten: 
 
a) Wird ein VAR-Parameter in der Definition der Prozedur vorgeschrieben (wie 
   z.B. im Programm 20), darf kein Ausdruck als aktueller Parameter "überge- 
   ben" werden, weil an einen Ausdruck nichts zugewiesen werden kann. 
   Gegenbeispiel: 
 
     vertausche ( eins * zwei, drei) 
 
   Ausdrücke haben - wie bereits erwähnt - das Accessrecht CONST. 
 
b) Wird ein CONST-Parameter verlangt, dann darf in diesem Fall ein Ausdruck 
   als aktueller Parameter geschrieben werden. Aber auch ein VAR-Datenobjekt 
   darf angegeben werden. In diesem Fall wird eine Wandlung des Accessrechts 
   (CONSTing) vorgenommen: der aktuelle Parameter erhält sozusagen für die 
   Zeit der Abarbeitung der Prozedur das Accessrecht CONST. 
 
Es ist auch möglich, Prozeduren als Parameter zu definieren, worauf wir aber 
hier nicht eingehen wollen. 
 
Eine Werte liefernde Prozedur erhält man, wenn der Prozedurrumpf einen Wert 
liefert, d.h. die letzte ausführbare Anweisung des Prozedurrumpfes muß einen 
Wert liefern (analog Werte liefernde Refinements) und der zu liefernde 
Datentyp vor den Prozedurkopf geschrieben wird. 
 
 
Programm 21: 
 
     INT PROC max (INT CONST a, b): 
        IF a > b 
          THEN a 
          ELSE b 
        END IF 
     END PROC max; 
 
     put (max (3, 4)) 
 
(In unserem Beispiel liefert die IF-Anweisung einen Wert. Das erfolgt da- 
durch, daß beide Zweige der Anweisung einen Wert liefern.) 
 
 
 
Neudefinierte Operatoren 
 
Neue, zusätzliche Operatoren können in ELAN wie Prozeduren definiert werden. 
Es ist nur notwendig, bei der Definition das Wort PROC gegen OP zu vertau- 
schen. Es sind aber auch nur 1 oder 2 Parameter bei Operatoren erlaubt. 
 
 
Programm 22a: 
 
     TEXT OP * (INT CONST mal, TEXT CONST t): 
        INT  VAR zaehler :: mal; 
        TEXT VAR ergebnis :: ""; 
        WHILE zaehler > 0 REP 
          ergebnis := ergebnis + t; 
          zaehler := zaehler - 1 
        END REP; 
        ergebnis 
     END OP *; 
 
Dieser Operator vervielfältigt TEXTe ( 2 * "ha" liefert "haha"). Der Name 
des Operators ist '*'. Man kann als Operatornamen die vorhandenen, bereits 
benutzten Sonderzeichen verwenden. In diesem Fall bekommt der neudefinierte 
Operator die gleiche Priorität wie der bereits vorhandene oder ein Schlüssel- 
wort. 
 
Der "Aufruf" eines Operators unterscheidet sich von einer Prozedur durch die 
infix-Schreibweise. Im übrigen gilt das für Prozeduren Gesagte. 
 
 
 
Optimierungen 
 
Optimierungen werden vorgenommen, wenn man mit den Laufzeiten bzw. Speicher- 
bedarf eines Programms nicht zufrieden ist. Kleinere, lokale Optimierungen 
sind meist nicht sinnvoll und notwendig und bringen mehr Fehler, als Ver- 
besserungen: 
 
 
Programm 22b: 
 
     TEXT OP * (INT CONST mal, TEXT CONST t): 
        INT  VAR i; 
        TEXT VAR ergebnis :: ""; 
        FOR i FROM 1 UPTO mal REP 
          ergebnis CAT t 
        END REP; 
        ergebnis 
     END OP *; 
 
Wir haben hier die WHILE-Schleife durch eine Zählschleife und 'ergebnis := 
ergebnis + t' durch 'ergebnis CAT t' ersetzt. Dies ist nur eine minimale 
Optimierung (wenn sie überhaupt etwas einbringt). Leider sind solche 
"Optimierungen" sehr häufig anzutreffen. Besser ist es, eine Lösung zu 
finden, die algorithmisch oder von den Datenstrukturen her prinzipiell 
besser ist. Wir haben dies für das Programm 22 getan. Lösungsidee: jeweilige 
Verdopplung eines Zwischentextes ("Russische Multiplikation"). 
 
 
Programm 22c: 
 
     TEXT OP * (INT CONST mal, TEXT CONST t): 
        INT  VAR zaehler :: mal; 
        TEXT VAR einer :: "", 
                 dopplung :: t; 
        IF fehlerhafter aufruf 
          THEN LEAVE * WITH "" 
          ELSE verdopplung 
        END IF; 
        dopplung + einer. 
 
        fehlerhafter aufruf: 
          zaehler < 1. 
 
        verdopplung: 
          WHILE zaehler > 1 REP 
            IF zaehler ist ungerade 
              THEN einer CAT t 
            END IF; 
            dopplung CAT dopplung; 
            zaehler := zaehler DIV 2 
          END REP. 
 
        zaehler ist ungerade: 
          zaehler MOD 2 = 1. 
 
     END OP *; 
 
 
In diesem Programm wurde eine Anweisung verwendet (LEAVE), die wir im 
folgenden Abschnitt erklären wollen. 
 
 
 
Das LEAVE-Konstrukt 
 
Das LEAVE-Konstrukt wird verwendet, um eine benannte Anweisung (Refinement, 
Prozedur oder Operator) vorzeitig zu verlassen. Es ist auch möglich, mehr- 
fach geschachtelte Refinements zu verlassen. Durch eine (optionale) 
WITH-Angabe kann auch ein wertelieferndes Refinement verlassen werden. 
 
 
 
Reihungen 
 
Wir haben bis jetzt bereits zusammengesetzte algorithmische Objekte kennen- 
gelernt, die man unter einem Namen als Ganzes ansprechen kann (Prozeduren). 
Die gleiche Möglichkeit gibt es auch bei Datenobjekten, wobei wir gleich- 
artige oder ungleichartige Objekte zu einem Objekt zusammenfassen können. 
Zuerst zu der Zusammenfassung gleichartiger Datenobjekte, die in ELAN eine 
Reihung (ROW) genannt wird. Die einzelnen Objekte einer Reihung werden 
Elemente genannt. Beispiel (Deklaration einer Reihung von 10 INT-Elementen): 
 
     ROW 10 INT VAR feld 
 
Die Angabe hinter dem Schlüsselwort ROW muß ein INT-Denoter sein (oder 
durch ein LET definierter Name). Dabei ist ROW 10 INT ein (neuer, von den 
elementaren unterschiedlicher) Datentyp, für den keine Operationen definiert 
sind, außer der Zuweisung. Das Accessrecht (VAR in unserem Beispiel) und der 
Name ('feld') gilt - wie bei den elementaren Datentypen - für diesen neuen 
Datentyp, also für alle 10 Elemente. 
 
Warum gibt es keine Operationen außer der Zuweisung? Das wird uns sehr 
schnell einsichtig, wenn wir uns vorstellen, daß es ja sehr viele Datentypen 
(zusätzlich zu den elementaren) gibt, weil Reihungen von jedem Datentyp 
gebildet werden können: 
 
     ROW 1 INT             ROW 1 REAL 
     ROW 2 INT             ROW 2 REAL 
     :                     : 
     ROW maxint INT        ROW maxint REAL 
 
     ROW 1 TEXT            ROW 1 BOOL 
     ROW 2 TEXT            ROW 2 BOOL 
     :                     : 
     ROW maxint TEXT       ROW maxint BOOL 
 
Für die elementaren INT-, REAL-, BOOL- und TEXT-Datentypen sind 
unterschiedliche Operationen definiert. Man müßte nun für jeden dieser 
zusammengesetzten Datentypen z.B. auch 'get'- und 'put'-Prozeduren 
schreiben, was allein vom Schreibaufwand sehr aufwendig wäre. Das ist der 
Grund dafür, daß es keine vorgegebene Operationen auf zusammengesetzte 
Datentypen gibt. 
 
Zugegebenermaßen könnte man mit solchen Datentypen, die nur über eine Opera- 
tion verfügen (Zuweisung), nicht sehr viel anfangen, wenn es nicht eine wei- 
tere vorgegebene Operation gäbe, die #ib#Subskription#ie#. Sie erlaubt es, 
auf die Elemente einer Reihung zuzugreifen und den Datentyp der Elemente 
"aufzudecken". Beispiel: 
 
     feld [3] 
 
bezieht sich auf das dritte Element der Reihung 'feld' und hat den Datentyp 
INT. Für INT-Objekte haben wir aber einige Operationen, mit denen wir 
arbeiten können. Beispiele: 
 
     feld [3] := 7; 
     feld [4] := feld [3] + 4; 
     ... 
 
Eine Subskription "schält" also vom Datentyp ein ROW ab und liefert ein 
Element der Reihung. Die Angabe der Nummer des Elements in der Reihung nennt 
man Subskript (in unserem Fall '3'). Der Subskript wird in ELAN in eckigen 
Klammern angegeben, um eine bessere Unterscheidung zu den runden Klammern in 
Ausdrücken zu erreichen. Ein subskribiertes ROW-Datenobjekt kann also über- 
all da verwendet werden, wo ein entsprechender Datentyp benötigt wird (Aus- 
nahme: Schleifenvariable). Als Beispiel zeigen wir zwei Prozeduren, die eine 
Reihung einlesen bzw. ausgeben: 
 
 
Programm 23: 
 
     PROC get (ROW 10 INT VAR feld): 
        INT VAR i; 
        FOR i FROM 1 UPTO 10 REP 
          put (i); put ("tes Element bitte:"); 
          get (feld [i]); 
          line 
        END REP 
     END PROC get; 
 
     PROC put (ROW 10 INT CONST feld): 
        INT VAR i; 
        FOR i FROM 1 UPTO 10 REP 
          put (i); put ("tes Element ist:"); 
          put (feld [i]); 
          line 
        END REP 
     END PROC put 
 
 
Wie bereits erwähnt, ist es erlaubt, Reihungen überall dort zu verwenden, wo 
auch die elementaren Datentypen verwandt werden können, also auch als 
Parameter. Zudem haben wir die generischen Eigenschaften von Prozeduren in 
ELAN bei der Benennung der Prozeduren benutzt. 
 
Diese beiden Prozeduren benutzen wir gleich im nächsten Programm, welches 
10 Werte einliest und die Summe berechnet: 
 
 
Programm 24: 
 
     ROW 10 INT VAR werte; 
     lies werte ein; 
     summiere sie; 
     drucke die summe und einzelwerte. 
 
     lies werte ein: 
        get (werte). 
 
     summiere sie: 
        INT VAR summe :: 0, i; 
        FOR i FROM 1 UPTO 10 REP 
          summe INCR werte [i] 
        END REP. 
 
     drucke die summe und einzelwerte: 
        put (werte); 
        line; 
        put ("Summe:"); put (summe). 
 
 
Aufgabe (HSG): 
 
     Wie kann man vermeiden, daß 'summe > maxint' ("overflow"-Bedingung) 
     wird? 
 
 
Oft benötigt man die Werte einer Reihung sortiert. Das Programm 25 zeigt 
einen (sehr dummen und ineffizienten) Sortieralgorithmus: 
 
 
Programm 25: 
 
     ROW 10 INT VAR wert; 
     lies die werte ein; 
     sortiere in eine zweite liste; 
     drucke die zweite liste. 
 
     lies die werte ein: 
        get (wert). 
 
     sortiere in eine zweite liste: 
        INT VAR i; 
        FOR i FROM 1 UPTO 10 REP 
          suche kleinstes element aus der werte liste; 
          bringe dieses in die zweite liste; 
          entferne es aus der werte liste 
        END REP. 
 
     suche kleinstes element aus der werte liste: 
     INT VAR kleinstes element :: maxint, position kleinstes element :: 0, k; 
        FOR k FROM 1 UPTO 10 REP 
          IF wert [k] < kleinstes element 
            THEN kleinstes element := wert [k]; 
                 position kleinstes element := k 
          END IF 
        END REP. 
 
     bringe dieses in die zweite liste: 
        ROW 10 INT VAR liste2; 
        liste2 [i] := kleinstes element. 
 
     entferne es aus der werte liste: 
        wert [position kleinstes element] := maxint. 
 
     drucke die zweite liste: 
        put (liste2). 
 
Anmerkung: Bei diesem einfachen Sortieralgorithmus (der übrigens "lineare 
Auswahl" heißt), wurde der Wert 'maxint' als zulässiger Wert ausgeschlossen. 
Der  Algorithmus ist ziemlich der schlechteste, den wir uns ausdenken können. 
Einmal braucht er den doppelten Speicherplatz für die zu sortierende Liste, 
andererseits sind für N Werte N*N Durchläufe durch die Liste notwendig (man 
sagt, der Algorithmus ist von der Ordnung N Quadrat). 
 
Da es möglich ist, von jedem Datentyp eine Reihung zu bilden, kann man 
natürlich auch von einer Reihung eine Reihung bilden: 
 
     ROW 5 ROW 10 INT VAR matrix 
 
Für eine "doppelte" Reihung gilt das für "einfache" Reihungen gesagte. 
Wiederum existieren keine Operationen für dieses Datenobjekt (außer der 
Zuweisung), jedoch ist es durch Subskription möglich, auf die Elemente zuzu- 
greifen: 
 
     matrix [3] 
 
liefert ein Datenobjekt mit dem Datentyp ROW 10 INT, für den wir bereits in 
Programm 23 die Prozeduren 'get' und 'put' geschrieben haben, die wir 
verwenden können: 
 
     get (matrix [4]) 
 
Subskribieren wir jedoch 'matrix' nochmals, so erhalten wir ein INT: 
 
     matrix [2] [8] 
 
(jede Subskription "schält" von Außen ein ROW vom Datentyp ab). 
 
 
Aufgabe (HSG): 
 
     a) Geben Sie Datentyp, Accessrecht und Name der folgenden Datenobjekte 
        an: 
           ROW 17 INT CONST alpha; 
           ROW 3 ROW 4 TEXT VAR matrix; 
           ... 
           beta [3] := 7; 
           gamma [4] := gamma [5] 
 
     b) Was führt zu Fehlern? Wenn ja, warum? 
           ROW 17 INT VAR alpha; 
           ROW 3 ROW 4 TEXT VAR beta, gamma; 
           ROW 4 ROW 3 TEXT CONST delta; 
           INT VAR x :: 7; 
           ROW x BOOL VAR y; 
           get (alpha); 
           get (beta [7]); 
           FOR x FROM 1 UPTO 3 REP 
             get (beta [x]) 
           END REP; 
           beta := delta; 
           delta [1] [2] := "mist"; 
           beta := gamma; 
           beta [3] := gamma [3]; 
           get (beta [1] [1]); 
           gamma [1] [5] := beta [1] [1] + "ELAN" 
           x := alpha [3]; 
           x := 20; 
           alpha [x] := alpha [3] + 7 
Übungsziel: Umgang mit Reihungen 
 
 
 
Strukturen 
 
Strukturen sind Datenverbunde wie Reihungen, aber die Komponenten können 
ungleichartige Datentypen haben. Die Komponenten von Strukturen heißen 
Felder (bei Reihungen: Elemente) und der Zugriff auf ein Feld Selektion 
(Reihungen: Subskription). Eine Struktur ist - genauso wie bei Reihungen - 
ein eigener Datentyp, der in einer Deklaration angegeben werden muß. 
Beispiel: 
 
     STRUCT (TEXT name, INT alter) VAR ich 
 
Wiederum existieren keine Operationen auf Strukturen außer der Zuweisung und 
der Selektion, die es erlaubt, Komponenten aus einer Struktur herauszulösen: 
 
     ich . name 
     ich . alter 
 
Die erste Selektion liefert einen TEXT-, die zweite ein INT-Datenobjekt. Mit 
diesen (selektierten) Datenobjekten kann - wie gewohnt - gearbeitet werden 
(Ausnahme: nicht als Schleifenvariable). 
 
Zum Datentyp einer Struktur gehören auch die Feldnamen: 
 
     STRUCT (TEXT produkt name, INT artikel nr) VAR erzeugnis 
 
ist ein anderer Datentyp als im ersten Beispiel dieses Abschnitts. Für 
Strukturen - genauso wie bei Reihungen - kann man sich neue Operationen 
definieren. Im folgenden Programm definieren wir für eine Struktur, die 
Personen beschreibt, die Operationen 'put', 'get' und den dyadischen 
Operator HEIRATET. Anschließend werden drei Paare verHEIRATET. 
 
 
Programm 26a: 
 
     PROC get (STRUCT (TEXT name, vorname, INT alter) VAR p): 
        put ("bitte Nachname:"); get ( p.name); 
        put ("bitte Vorname:");  get ( p.vorname); 
        put ("bitte Alter:");    get ( p.alter); 
        line 
     END PROC get; 
 
     PROC put (STRUCT (TEXT name, vorname, INT alter) CONST p): 
        put (p.vorname); put (p.name); 
        put ("ist"); 
        put (p.alter); 
        put ("Jahre alt"); 
        line 
     END PROC put; 
 
     OP HEIRATET 
        (STRUCT (TEXT name, vorname, INT alter) VAR w, 
         STRUCT (TEXT name, vorname, INT alter) CONST m): 
        w.name := m.name 
     END OP HEIRATET; 
 
     ROW 3 STRUCT (TEXT name, vorname, INT alter) VAR frau, mann; 
 
     personendaten einlesen; 
     heiraten lassen; 
     paardaten ausgeben. 
 
     personendaten einlesen: 
        INT VAR i; 
        FOR i FROM 1 UPTO 3 REP 
          get (frau [i]); 
          get (mann [i]) 
        END REP. 
 
     heiraten lassen: 
        FOR i FROM 1 UPTO 3 REP 
          frau [i] HEIRATET mann [i] 
        END REP. 
 
     paardaten ausgeben: 
        FOR i FROM 1 UPTO 3 REP 
          put (frau [i]); 
          put ("hat geheiratet:"); line; 
          put (mann [i]); line 
        END REP. 
 
Reihungen und Strukturen dürfen miteinander kombiniert werden, d.h. es darf 
eine Reihung in einer Struktur erscheinen oder es darf eine Reihung von einer 
Struktur vorgenommen werden. Selektion und Subskription sind in diesen Fällen 
in der Reihenfolge vorzunehmen, wie die Datentypen aufgebaut wurden (von 
außen nach innen). 
 
 
Aufgabe (HSG): 
 
     In ELAN heissen 
        a1) Datenverbunde gleichartiger Komponenten: 
        a2) Datenverbunde ungleichartiger Komponenten: 
        b1) die Komponenten eines ROWs: 
        b2) die Komponenten eines STRUCTs: 
        c1) die Zugriffe auf die Komponenten eines ROWs: 
        c2) die Zugriffe auf die Komponenten eines STRUCTs: 
Übungsziel: Begriffe von ROWs und STRUCTs kennenlernen 
 
 
 
LET-Konstrukt 
 
Wie wir in Programm 26 gesehen haben, ist die Verwendung von Strukturen 
oder auch Reihungen manchmal schreibaufwendig. Mit dem LET-Konstrukt darf 
man Datentypen (und Denotern) einen Namen geben. Dieser Name steht als 
Abkürzung und verringert so die Schreibarbeit. Zusätzlich wird durch die 
Namensgebung die Lesbarkeit des Programms erhöht. Beispiel: 
 
 
Programm 26b: 
 
     LET PERSON = STRUCT (TEXT name, vorname, INT alter); 
 
     PROC get (PERSON VAR p): 
        put ("bitte Nachname:"); get ( p.name); 
        put ("bitte Vorname:");  get ( p.vorname); 
        put ("bitte Alter:");    get ( p.alter); 
        line 
     END PROC get; 
 
     PROC put (PERSON CONST p): 
        put (p.vorname); put (p.name); put ("ist"); 
        put (p.alter);   put ("Jahre alt"); line 
     END PROC put; 
 
     OP HEIRATET (PERSON VAR f, PERSON CONST m): 
        f.name := m.name 
     END OP HEIRATET; 
 
     ROW 3 PERSON VAR mann, frau; 
     ... 
 
Überall wo der abzukürzende Datentyp verwandt werden kann, kann PERSON 
benutzt werden. Wohlgemerkt: PERSON ist kein neuer Datentyp, sondern nur ein 
Name, der für STRUCT (....) steht. Der Zugriff auf die Komponenten des 
abgekürzten Datentyps bleibt erhalten (was bei abstrakten Datentypen, die wir 
etwas später erklären, nicht mehr der Fall ist). 
 
Neben der Funktion der Abkürzung von Datentypen kann das LET-Konstrukt 
auch für die Namensgebung für die Denoter verwandt werden. Beispiele: 
 
     LET pi = 3.14159; 
     LET blank = " "; 
     LET anzahl = 27 
 
Der Einsatz von LET-Namen für INT-Denoter macht es möglich, Programme 
leicht zu ändern: 
 
 
Programm 26c: 
 
     LET anzahl paare = 3; 
     ROW anzahl paare PERSON VAR frau, mann; 
 
     personendaten einlesen; 
     heiraten lassen; 
     paardaten ausgeben. 
 
     personendaten einlesen: 
        INT VAR i; 
        FOR i FROM 1 UPTO anzahl paare REP 
          get (frau [i]); 
          get (mann [i]) 
        END REP. 
     ... 
 
Ebenso wie die Abkürzung von Datentypen (LET PERSON = STRUCT (...)) wird 
im obigen Beispiel für den Namen 'anzahl paare' bei jedem Auftreten der 
Denoter '3' vom ELAN-Compiler eingesetzt. Um nun (z.B.) 27 Paare "heiraten" 
zu lassen, brauchen wir nur die LET-Anweisung in '27' zu verändern... 
(Scheidungen erfordern etwas mehr Aufwand). 
 
 
Aufgabe (HSG): 
 
     Was ist falsch? 
        LET anz = 5, 
            max = 5*5, 
            MAT = ROW anz ROW anz TEXT; 
 
        PROC get (MAT CONST m): 
           FOR i FROM 1 UPTO max REP 
             get (m [i]) 
           END REP 
        END PROC get; 
 
        MAT VAR x,y; 
        get (x); 
        x := y + 1 
 
 
Aufgabe (HSG): 
 
     Schreibe ein Programm, das mit den Deklarationen 
        LET anz = 5, 
            VEC = ROW anz INT, 
            MAT = ROW anz VEC; 
     folgende Prozeduren realisiert: 
        PROC get (VEC VAR v) 
        PROC get (MAT VAR m) 
        PROC put (VEC CONST v) 
        PROC put (MAT CONST m) 
        INT PROC reihensumme (VEC CONST v) 
Übungsziel: Reihungen als Parameter 
 
 
 
Denoter für Datenverbunde: Konstruktoren 
 
Denoter für die elementaren Datentypen haben wir kennengelernt. Oft ergibt 
sich auch die Notwendigkeit (z.B. bei Initialisierungen), Datenverbunde in 
einem Programm Werte zu geben. Das kann durch normale Zuweisungen erfolgen. 
Beispiel: 
 
     LET PERSON = STRUCT (TEXT name, vorname, INT alter); 
 
     PERSON VAR mann; 
 
     mann.name := "meier"; 
     mann.vorname := "egon"; 
     mann.alter := 27 
 
Aber man möchte auch Denoter für Datenverbunde z.B. in Ausdrücken verwenden, 
was durch die Konstruktoren ermöglicht wird. Beispiel: 
 
     LET PERSON = STRUCT (TEXT name, vorname, INT alter); 
 
     PERSON VAR mann, frau; 
 
     frau := PERSON : ( "niemeyer", "einfalt", 65); 
     frau HEIRATET PERSON : ( "meier", "egon", 27) 
 
Ein Konstruktor ist also ein Mechanismus, um ein Datenobjekt eines Datenver- 
bundes in einem Programm zu notieren. Ein Konstruktor besteht aus der Angabe 
des Datentyps (der auch durch einen LET-Namen abgekürzt sein darf), einem 
Doppelpunkt und den in Klammern eingefaßten Komponenten (hier Denoter). 
Besteht eine der Komponenten wiederum aus einem Datenverbund, muß inner- 
halb des Konstruktors wiederum ein Konstruktor eingesetzt werden usw. Kon- 
struktoren sind natürlich für Reihungen auch möglich: 
 
     ROW 7 INT VAR feld; 
     feld := ROW 7 INT : ( 1, 2, 3, 4, 5, 6, 7); 
 
 
Aufgabe (HSG): 
 
     Geben Sie Datentyp, Accessrecht und Name der folgenden Datenobjekte an: 
        STRUCT (INT alter, TEXT name) VAR mensch; 
        STRUCT (INT jahrgang, ROW 2 TEXT lage) CONST wein; 
        ROW 100 STRUCT (PERSON p, NUMMER n) VAR betriebsangehoeriger; 
        STRUCT (INT anz terminals, STRUCT (TEXT systemname, INT version) art) 
                                                         CONST betriebsystem; 
        mensch := ...; 
        betriebsangehoeriger [2] := ...; 
        betriebsangehoeriger [2]. n := NUMMER: (...); 
        betriebsystem.art.systemname := "EUMEL"; 
        wein.lage := ROW 2 TEXT: ("Loire", "Frankreich"); 
        wein.lage [1] := "Kroever Nacktarsch"; 
Übungsziel: Umgang mit Strukturen. 
 
 
 
Rekursive Prozeduren und Operatoren 
 
Alle Prozeduren und Operatoren dürfen in ELAN rekursiv sein. 
 
 
Programm 27: 
 
     INT PROC fakultaet (INT CONST n): 
        IF n > 0 
          THEN fakultaet (n-1) * n 
          ELSE 1 
        END IF 
     END PROC fakultaet 
 
Dieses Beispiel ist aber (leider) kein gutes Beispiel für eine Rekursion, 
denn das Programm kann leicht in eine iterative Version umgewandelt werden: 
 
 
Programm 28: 
 
     INT PROC fakultaet (INT CONST n): 
        INT VAR prod :: 1, i; 
        FOR i FROM 2 UPTO n REP 
          prod := prod * i 
        END REP; 
        prod 
     END PROC fakultaet 
 
Die Umwandlung von einem rekursiven Programm in ein iteratives ist übrigens 
immer möglich, jedoch oft nicht so einfach wie in diesem Fall. Beispiel 
(Ackermann-Funktion): 
 
 
Programm 29: 
 
     INT PROC acker (INT CONST m, n): 
        IF m = 0 
          THEN n + 1 
        ELIF n = 0 
          THEN acker (m-1, 0) 
          ELSE acker (m - 1, acker (m, n - 1)) 
        ENDIF 
     END PROC acker 
 
 
Aufgabe (HSG): 
 
     a) Beschreibe die Unterschiede zwischen Iteration und Rekursion. Worauf 
        muß man bei Rekursionen achten? 
     b) Wie groß ist der Wert von 'acker (2, 2)'? Hilfreicher Tip: stelle 
        dabei eine Tabelle auf! 
     c) Zudem enthält die Programmierung der Ackermann-Funktion (mindestens) 
        einen Fehler. Welchen? 
Übungsziel: Umgang mit Rekursion 
 
 
Das eigentliche Einsatzgebiet von rekursiven Algorithmen liegt aber bei den 
'backtrack'-Verfahren. Diese werden eingesetzt, wenn eine exakte algorithmi- 
sche Lösung nicht bekannt ist oder nicht gefunden werden kann und man ver- 
schiedene Versuche machen muß, um zu einem Ziel (oder Lösung) zu gelangen. 
 
Als Beispielprogramm zeigen wir das Spiel "Maus sucht Käse". In einem Laby- 
rinth (realisiert durch eine Reihung von einer Reihung), das mit Hindernis- 
sen bestückt ist, wurde ein Käse versteckt. Eine sehr dumme Maus sucht 
systematisch die umliegenden Felder (in allen vier Himmelsrichtungen) nach 
dem Käse ab. Ist sie auf einem neuen Feld und ist das Feld frei, sucht sie 
erneut. Felder, auf denen sie bereits war, werden von ihr markiert. Da die 
Maus sehr kurzsichtig ist und nicht richtig riechen kann, bemerkt sie den 
Käse erst, wenn sie sozusagen mit allen vier Pfoten in ihm gelandet ist 
(analog bei Hindernissen, die nicht überklettert werden können). Damit die 
Maus nicht aus dem Labyrinth entfliehen kann, wird der Rand als Hindernis 
angesehen. 
 
 
(Teil-) Programm 30: 
 
     PROC suche weg (INT CONST x, y): 
        IF labyrinth [x] [y] = kaese 
          THEN kaese gefunden 
        ELIF labyrinth [x] [y] = frei 
          THEN suche weiter 
        END IF. 
 
        suche weiter: 
           labyrinth [x] [y] := markiert; 
           INT VAR richtung; 
           FOR richtung FROM osten UPTO sueden REP 
             versuche diese richtung 
           END REP; 
           labyrinth [x] [y] := frei. 
 
        versuche diese richtung: 
           IF richtung = osten 
             THEN suche weg (x + 1, y) 
           ELIF richtung = norden 
             THEN suche weg (x, y + 1) 
           ... 
 
 
 
Dateien 
 
Dateien werden benötigt, wenn 
 
- Daten über die Abarbeitungszeit eines Programms aufbewahrt werden sollen; 
- der Zeitpunkt oder Ort der Datenerfassung nicht mit dem Zeitpunkt oder Ort 
  der Datenverarbeitung übereinstimmt; 
- die gesamte Datenmenge nicht auf einmal in den Zentralspeicher eines 
  Rechners paßt; 
- die Anzahl und/oder Art der Daten nicht von vornherein bekannt sind. 
 
Eine Datei ("file") ist eine Zusammenfassung von Daten, die auf Massenspei- 
chern aufbewahrt wird. Dateien sind in bestimmten Informationsmengen, den 
Sätzen ("records") organisiert. 
 
In ELAN gibt es zwei Arten von Dateien: 
 
a) FILE: sequentielle Dateien. Die Sätze können nur sequentiell gelesen bzw. 
   geschrieben werden. Eine Positionierung ist nur zum nächsten Satz möglich. 
b) DIRFILE: indexsequentielle Dateien. Die Positionierung erfolgt direkt mit 
   Hilfe eines Schlüssels ("key") oder Index, kann aber auch sequentiell 
   vorgenommen werden. 
 
   Wichtig: 
   DIRFILEs sind auf dem EUMEL-System nicht implementiert! Deswegen wird 
   auf diesen Dateityp hier nicht weiter eingegangen. 
 
Dateien werden normalerweise von dem #ib#Betriebsystem#ie# eines Rechners 
aufbewahrt und verwaltet. Somit ist eine Verbindung von einem ELAN-Programm, 
in dem eine Datei unter einem Namen - wie jedes andere Datenobjekt auch - 
angesprochen werden soll, und dem Betriebsystem notwendig. Dies erfolgt durch 
sogenannte Assoziierungsprozeduren. Beispiele: 
 
     FILE VAR meine datei :: sequential file (output, "xyz"); 
     FILE CONST eine andere datei :: sequential file (input, "abc") 
 
Die Assoziierungsprozedur heißt 'sequential file' für FILEs. Der erste 
Parameter einer Assoziierungsprozedur gibt immer die sogenannte Betriebs- 
richtung ("TRANSPUTDIRECTION") an. Es gibt folgende Betriebsrichtungen: 
 
   input       nur Lesen der Datei 
   output      nur Schreiben der Datei 
 
   Anmerkung: 
   Im EUMEL-System gibt es noch die Betriebsrichtung 'modify', die es er- 
   laubt, beliebig zu positionieren, Sätze zu löschen und/oder einzufügen 
   usw. Dafür gibt es keine DIRFILEs. Siehe dazu auch das Kapitel über 
   Dateien in diesem Benutzerhandbuch. 
 
Der zweite Parameter einer Assoziierungsprozedur gibt an, unter welchem 
Namen die Datei in dem Betriebsystem bekannt ist. Mit Hilfe dieses Namens 
wird die Datei an das Datenobjekt gekoppelt, das bei der FILE-Deklaration im 
Programm erzeugt wurde. 
 
Welche Operationen sind nun für Dateien zugelassen? Wir beschreiben die 
wichtigsten für die beiden Dateiarten und Betriebsrichtungen getrennt: 
 
a) FILE mit 'input' (nur lesen): 
 
   PROC getline (FILE CONST f, TEXT VAR zeile) 
   PROC get (FILE CONST f, TEXT VAR t) 
   PROC get (FILE CONST f, REAL VAR r) 
   PROC get (FILE CONST f, INT VAR i) 
   BOOL PROC eof (FILE CONST f) 
   (* Abfrageprozedur auf das Ende eines FILEs (letzter Satz) *) 
 
   Als Beispiel zeigen wir ein Programm, welches eine Datei liest und auf 
   dem Ausgabemedium ausgibt. 
 
 
   Programm 31: 
 
     FILE CONST f :: sequential file (input, "datei1"); 
     TEXT VAR satz; 
     WHILE NOT eof (f) REP 
        getline (f, satz); 
        put (satz); line 
     END REP. 
 
b) FILE mit 'output' (nur schreiben): 
 
   PROC putline (FILE VAR f, TEXT CONST zeile) 
   PROC put     (FILE VAR f, TEXT CONST t) 
   PROC put     (FILE VAR f, REAL CONST r) 
   PROC put     (FILE VAR f, INT  CONST i) 
   PROC line    (FILE VAR f, INT  CONST zeilenzahl) 
 
 
Aufgabe (HSG): 
 
     a) Das Arbeiten mit Dateien ist manchmal notwendig, weil ... 
     b) Wenn man mit Dateien in ELAN arbeitet, sind Assoziierungsprozeduren 
        notwendig. 
        b1) Wie heißen diese? 
        b2) Was gibt man in diesen an? 
     c) Welche Betriebsrichtungen gibt es bei FILES und was bewirken sie? 
Übungsziel: Datei-Begriffe 
 
 
Aufgabe (TSW): 
 
     Welche Fehler befinden sich in den folgenden Programmfragmenten? 
     a) FILE VAR f :: sequential file (input, "MIST"); 
        TEXT VAR zeile; 
        REP 
          getline (f, zeile); 
          ... 
        UNTIL eof (f) END REP 
 
     b) FILE VAR f :: sequential file (output, "NOCHMAL"); 
        TEXT VAR zeile; 
        REP 
          getline (f, zeile); 
          put (zeile) 
        UNTIL eof (f) END REP 
 
     c) FILE VAR f :: sequential file (input, "VERDAMMT"), 
                 g :: sequential file (input, "DAEMLICH"); 
        TEXT VAR zeile; 
        kopiere g in f. 
 
        kopiere g in f: 
        FOR i FROM 1 UPTO 100 REP 
          getline (g, zeile); 
          putline (f, zeile) 
        UNTIL eof (g) END REP. 
Übungsziel: Arbeiten mit Dateien 
 
 
 
Programmstruktur 1 
 
Bis jetzt haben wir noch nicht vollständig erklärt, wie ein ELAN-Programm 
formal als Ganzes aufgebaut sein muß, d.h. wie und in welcher Reihenfolge 
die Anweisungen, Refinements, Prozeduren und Deklarationen geschrieben 
werden müssen. 
 
Ein ELAN-Programm kann aus mehreren Moduln (Bausteinen) aufgebaut sein, 
die in ELAN PACKETs genannt werden. Das letzte PACKET wird "main packet" 
genannt, weil in diesem das eigentliche Benutzerprogramm (Hauptprogramm) 
enthalten ist. In diesem Abschnitt wollen wir uns nur mit dem Aufbau eines 
solchen PACKETs beschäftigen. Wir werden dabei nicht alle Möglichkeiten 
besprechen, sondern nur die wichtigsten Anwendungen beschreiben, mit einer 
Empfehlung, in welcher Reihenfolge die Elemente geschrieben werden sollten. 
 
Ein "main packet" kann aus folgenden Elementen bestehen: 
 
a) Deklarationen und Anweisungen. Diese müssen nicht in einer bestimmten 
   Reihenfolge im Programm erscheinen, sondern es ist möglich, erst in dem 
   Augenblick zu deklarieren, wenn z.B. eine neue Variable benötigt wird. Es 
   ist jedoch gute Programmierpraxis, die meisten Deklarationen an den 
   Anfang eines Programms (außer z.B. Datenobjekte, die nur lokal oder 
   kurzfristig benötigt werden, wie Hilfsvariablen oder Laufvariablen) zu 
   plazieren. 
 
     <Deklarationen> ; 
     <Anweisungen> 
 
b) Deklarationen, Refinements und Anweisungen. In diesem Fall ist es notwen- 
   dig, die Refinements hintereinander zu plazieren. Refinement-Aufrufe 
   und/oder Anweisungen sollten textuell vorher erscheinen. Die Refinements 
   werden durch einen Punkt von den Aufrufen getrennt: 
 
     <Deklarationen> ; 
     <Refinement-Aufrufe und/oder Anweisungen> . 
     <Refinements> 
 
   Innerhalb der Refinements sind Anweisungen und/oder Deklarationen möglich. 
 
c) Deklarationen, Prozeduren und Anweisungen. Werden Prozeduren vereinbart, 
   sollte man sie nach den Deklarationen plazieren. Danach sollten die 
   Anweisungen folgen: 
 
     <Deklarationen> ; 
     <Prozeduren> ; 
     <Anweisungen> 
 
   Mehrere (parallele) Prozeduren werden durch ";" voneinander getrennt. In 
   diesem Fall sind die Datenobjekte aus den Deklarationen außerhalb von 
   Prozeduren statisch, d.h. während der gesamten Laufzeit des Programm 
   vorhanden. Solche Datenobjekte werden auch PACKET-Daten genannt.Im 
   Gegensatz dazu sind die Datenobjekte aus Deklarationen in Prozeduren 
   dynamische Datenobjekte, die nur während der Bearbeitungszeit der 
   Prozedur existieren. Innerhalb einer Prozedur dürfen wiederum Refinements 
   verwendet werden. Ein Prozedur-Rumpf hat also den formalen Aufbau wie 
   unter a) oder b) geschildert. 
 
   Die Refinements und Datenobjekte, die innerhalb einer Prozedur deklariert 
   wurden, sind lokal zu dieser Prozedur, d.h. können von außerhalb nicht 
   angesprochen werden. 
 
d) Deklarationen, Prozeduren, Anweisungen und PACKET-Refinements. 
   Zusätzlich zu der Möglichkeit c) ist es erlaubt, neben den Anweisungen 
   außerhalb einer Prozedur auch Refinements zu verwenden: 
 
     <Deklarationen> ; 
     <Prozeduren> ; 
     <Anweisungen> . 
     <Refinements> 
 
   Diese Refinements können nun in Anweisungen außerhalb der Prozeduren 
   benutzt werden oder auch durch die Prozeduren (im letzteren Fall spricht 
   man analog zu globalen PACKET-Daten auch von PACKET-Refinements oder 
   globalen Refinements). In PACKET-Refinements dürfen natürlich keine 
   Datenobjekte verwandt werden, die lokal zu einer Prozedur sind. 
 
 
 
Moduln (PACKETs) 
 
PACKETs sind in ELAN eine Zusammenfassung von Datenobjekten, Prozeduren/ 
Operatoren und Datentypen. Man kann sich ein PACKET als ein 'main packet' 
mit Zusätzen vorstellen. PACKETs können separat übersetzt werden, so 
daß der "Zusammenbau" eines umfangreichen Programms aus mehreren PACKETs 
möglich ist. 
 
Elemente eines PACKETs (Prozeduren/Operatoren, Datentypen) können außerhalb 
des PACKETs nur angesprochen werden, wenn sie in der Schnittstelle des 
PACKETs, die auch "interface" genannt wird, aufgeführt wird. Mit anderen 
Worten: es können alle Elemente eines PACKETs von außen nicht angesprochen 
werden, sofern sie nicht über die Schnittstelle "nach außen gereicht" wird. 
Damit wird gewährleistet, daß mehrere Programmierer an einem gemeinsamen 
Projekt arbeiten können und daß eine gemeinsame Benutzung (oder Störung) von 
Programmteilen nur über die in den Schnittstellen von PACKETs aufgeführten 
Objekte erfolgen kann. 
 
Im Gegensatz zu einer Prozedur kann ein PACKET nicht aufgerufen werden (nur 
die Elemente der Schnittstelle können benutzt werden). Beispiel: 
 
 
Programm 32: 
 
     PACKET fuer eine prozedur DEFINES swap: 
 
     PROC swap (INT VAR a, b): 
        INT CONST x :: a; 
        b := a; 
        a := x 
     END PROC swap 
 
     END PACKET fuer eine prozedur 
 
Dies ist ein PACKET, das eine Tausch-Prozedur für INT-Datenobjekte bereit- 
stellt. Das PACKET kann übersetzt werden und dem ELAN-Compiler bekannt 
gemacht werden (EUMEL: "insertieren"). Ist das geschehen, kann man 'swap' 
wie alle anderen Prozeduren (z.B. 'put', 'get') in einem Programm verwenden. 
Tatsächlich werden die meisten Prozeduren und Operatoren (aber auch einige 
Datentypen), die in ELAN zur Verfügung stehen, nicht durch den ELAN-Compiler 
realisiert, sondern durch solche PACKETs. Um solche Objekte einigermaßen 
zu standardisieren, wurde in der ELAN-Sprachbeschreibung festgelegt, welche 
Datentypen, Prozeduren und Operatoren in jedem ELAN-System vorhanden 
sein müssen. Solche PACKETs werden Standard-Pakete genannt. Jeder Installa- 
tion - aber auch jedem Benutzer - steht es jedoch frei, zu den Standard- 
Paketen zusätzliche PACKETs mit in den Compiler aufzunehmen und damit den 
ELAN-Sprachumfang zu erweitern. 
 
Ein ELAN-PACKET beginnt mit dem PACKET-Schlüsselwort. Danach folgt der Name 
des PACKETs (der am Ende des PACKETs hinter END PACKET wieder erscheinen 
muß), gefolgt von der DEFINES-Liste. In dieser Schnittstelle werden die Ob- 
jekte angegeben, die nachfolgenden PACKETs zur Verfügung gestellt werden 
sollen. 
 
In der Schnittstelle werden Prozeduren/Operatoren nur mit ihrem Namen ange- 
geben. Weiterhin können Datentypen und mit CONST vereinbarte Datenobjekte 
in der Schnittstelle aufgeführt werden, aber keine VAR-Datenobjekte, weil 
diese sonst über PACKET-Grenzen hinweg verändert werden könnten. 
 
Im obigen Programm 32 haben wir ein PACKET in der Funktion als spracher- 
weiterndes Instrument gezeigt. Im folgenden Beispiel zeigen wir ein Programm, 
in dem das PACKET-Konzept verwandt wird, um Datenobjekte vor unbefugten 
Zugriff zu schützen. 
 
 
Programm 33: 
 
     PACKET stack handling DEFINES push, pop, init stack: 
 
     LET max = 1000; 
     ROW max INT VAR stack; 
     INT VAR stack pointer; 
 
     PROC init stack: 
        stack pointer := 0 
     END PROC init stack; 
 
     PROC push (INT CONST dazu wert): 
        stack pointer INCR 1; 
        IF stack pointer > max 
          THEN errorstop ("stack overflow") 
          ELSE stack [stack pointer] := dazu wert 
        END IF 
     END PROC push; 
 
     PROC pop (INT VAR von wert): 
        IF stack pointer = 0 
          THEN errorstop ("stack empty") 
          ELSE von wert := stack [stack pointer]; 
               stack pointer DECR 1 
        END IF 
     END PROC pop 
 
     END PACKET stack handling; 
 
Nun kann man den Stack über die Prozeduren 'init stack', 'push' und 'pop' 
benutzen (in einem 'main packet'). 
 
 
Programm 34: 
 
     init stack; 
     werte einlesen und pushen; 
     werte poppen und ausgeben. 
 
     werte einlesen und pushen: 
        INT VAR anzahl :: 0, wert; 
        REP 
           get (wert); 
           push (wert); 
           anzahl INCR 1 
        UNTIL ende kriterium END REP. 
 
     werte poppen und ausgeben: 
        INT VAR i; 
        FOR i FROM 1 UPTO anzahl REP 
           pop (wert); 
           put (wert) 
        END REP. 
 
Die Datenobjekte 'stack' und 'stack pointer' haben nur Gültigkeit innerhalb 
des PACKETs 'stack handling'. Anweisungen wie z.B. 
 
     put (stack [3]); 
     stack [27] := 5 
 
außerhalb des PACKETs 'stack handling' sind also verboten und werden vom 
ELAN-Compiler entdeckt. 
 
Ein PACKET bietet also auch einen gewissen Schutz vor fehlerhafter Verwen- 
dung von Programmen und Datenobjekten. Wichtig ist weiterhin, daß die Reali- 
sierung des Stacks ohne weiteres geändert werden kann, ohne daß Benutzer- 
programme im 'main packet' geändert werden müssen, sofern die Schnittstelle 
nicht verändert wird. Beispielsweise kann man sich entschließen, den Stack 
nicht durch eine Reihung, sondern durch eine gekettete Liste zu realisieren. 
Davon bleibt ein Benutzerprogramm unberührt. 
 
Die letzte Funktion von PACKETs ist die Realisierung von abstrakten Daten- 
typen. Dazu müssen wir uns aber zuvor die Möglichkeiten anschauen, neue 
Datentypen zu definieren. 
 
 
 
Die Definition neuer Datentypen 
 
Im Gegensatz zur LET-Vereinbarung, bei der lediglich ein neuer Name für 
einen bereits vorhandenen Datentyp eingeführt wurde und bei der somit auch 
keine neuen Operationen definiert werden müssen (weil die Operationen für 
den abzukürzenden Datentyp verwandt werden können), wird durch eine TYPE- 
Vereinbarung ein gänzlich neuer Datentyp eingeführt. Im Gegensatz zu 
Strukturen und Reihungen stehen für solche Datentypen noch nicht einmal die 
Zuweisung zur Verfügung. Beispiel: 
 
     TYPE PERSON = STRUCT (TEXT name, vorname, INT alter) 
 
Ein solcher Datentyp kann wie auch alle anderen Datentypen verwandt werden 
(Deklarationen, Parameter, Werte liefernde Prozeduren, als Komponenten in 
Reihungen und Strukturen usw.). 
 
Der neudefinierte Datentyp wird abstrakter Datentyp genannt. Er kann mit 
Hilfe eines PACKETs (vergl. nächsten Abschnitt) anderen Programmteilen zur 
Verfügung gestellt werden. Die rechte Seite der TYPE-Vereinbarung wird 
"konkreter Typ", "Realisierung des Datentyps" oder Feinstruktur genannt. 
 
Um neue Operatoren und/oder Prozeduren für einen abstrakten Datentyp zu 
schreiben, ist es möglich, auf die Komponenten des Datentyps (also auf die 
Feinstruktur) mit Hilfe des Konkretisierers zuzugreifen. Der Konkretisierer 
arbeitet ähnlich wie die Subskription oder Selektion: er ermöglicht eine 
typmäßige Umbetrachtung vom abstrakten Typ zum Datentyp der Feinstruktur. 
Beispiel: 
 
     TYPE MONAT = INT; 
 
     PROC put (MONAT CONST m): 
        put ( CONCR (m)) 
     END PROC put; 
 
Der Konkretisierer ist bei Feinstrukturen notwendig, die von elementarem 
Datentyp sind. Besteht dagegen die Feinstruktur aus Reihungen oder Struk- 
turen, dann wird durch eine Selektion oder Subskription eine implizite Kon- 
kretisierung vorgenommen. Beispiel: 
 
     TYPE LISTE = ROW 100 INT; 
 
     LISTE VAR personal nummer; 
     ... 
     personal nummer [3] := ... 
     (* das gleiche wie *) 
     CONCR (personal nummer) [3] := ... 
 
Denoter für neudefinierte Datentypen werden mit Hilfe des Konstruktors 
gebildet: 
 
     TYPE GEHALT = INT; 
 
     GEHALT VAR meins :: GEHALT : (1 000 000); 
 
Besteht die Feinstruktur aus einem Datenverbund, muß der Konstruktor u.U. 
mehrfach geschachtelt angewandt werden: 
 
     TYPE KOMPLEX = ROW 2 REAL; 
 
     KOMPLEX CONST x :: KOMPLEX : ( ROW 2 REAL : ( 1.0, 2.0)); 
 
 
 
Abstrakte Datentypen 
 
Auf die Feinstruktur über den Konkretisierer eines neudefinierten Datentyps 
darf nur in dem PACKET zugegriffen werden, in dem der Datentyp definiert 
wurde. Der Konstruktor kann ebenfalls nur in dem typdefinierenden PACKET 
verwandt werden. 
 
Wird der Datentyp über die Schnittstelle des PACKETs anderen Programmteilen 
zur Benutzung zur Verfügung gestellt, so müssen Operatoren und/oder Proze- 
duren für den Datentyp ebenfalls "herausgereicht" werden. Da dann der neude- 
finierte Datentyp genauso wie alle anderen Datentypen verwandt werden kann, 
aber die Komponenten nicht zugänglich sind, spricht man von abstrakten 
Datentypen. 
 
Welche Operationen sollten für einen abstrakten Datentyp zur Verfügung 
stehen? Obwohl das vom Einzelfall abhängt, werden meistens folgende 
Operationen und Prozeduren definiert: 
 
- 'get'- und 'put'-Prozeduren. 
- Zuweisung (auch für die Initialisierung notwendig). 
- Denotierungs-Prozedur (weil kein Konstruktor für den abstrakten Datentyp 
  außerhalb des definierenden PACKETs zur Verfügung steht) 
 
 
Programm 35: 
 
     PACKET widerstaende DEFINES WIDERSTAND, REIHE, PARALLEL, :=, get, put: 
 
     TYPE WIDERSTAND = INT; 
 
     OP := (WIDERSTAND VAR l, WIDERSTAND CONST r): 
        CONCR (l) := CONCR (r) 
     END OP :=; 
 
     PROC get (WIDERSTAND VAR w): 
        INT VAR i; 
        get (i); 
        w := WIDERSTAND : (i) 
     END PROC get; 
 
     PROC put (WIDERSTAND CONST w): 
        put (CONCR (w)) 
     END PROC put; 
 
     WIDERSTAND OP REIHE (WIDERSTAND CONST l, r): 
        WIDERSTAND : ( CONCR (l) + CONCR (r)) 
     END OP REIHE; 
 
     WIDERSTAND OP PARALLEL (WIDERSTAND CONST l, r): 
       WIDERSTAND : 
         ((CONCR (l) * CONCR (r)) DIV (CONCR (l) + CONCR (r))) 
     END OP PARALLEL 
 
     END PACKET widerstaende 
 
Dieses Programm realisiert den Datentyp WIDERSTAND und mit den Operationen 
eine Fachsprache, mit dem man nun leicht WIDERSTANDs-Netzwerke berechnen 
kann, wie z.B. folgendes: 
 
 
                              +--- R4 ---+ 
                              |          | 
        +--- R1 ---+          +--- R5 ---+ 
        |          |          |          | 
     ---+          +--- R3 ---+          +--- 
        |          |          |          | 
        +--- R2 ---+          +--- R6 ---+ 
                              |          | 
                              +--- R7 ---+ 
 
Zur Berechnung des Gesamtwiderstandes kann nun folgendes Programm 
geschrieben werden: 
 
     ROW 7 WIDERSTAND VAR r; 
     widerstaende einlesen; 
     gesamtwiderstand berechnen; 
     ergebnis ausgeben. 
 
     widerstaende einlesen: 
        INT VAR i; 
        FOR i FROM 1 UPTO 7 REP 
           put ("bitte widerstand R"); put (i); put (":"); 
           get (r [i]); 
        END REP. 
 
     gesamtwiderstand berechnen: 
        WIDERSTAND CONST rgesamt :: (r [1] PARALLEL r [2]) REIHE 
          r [3] REIHE (r [4] PARALLEL r [5] PARALLEL r [6] 
          PARALLEL r [7]). 
 
     ergebnis ausgeben: 
        line; 
        put (rgesamt). 
 
 
Aufgabe (HSG): 
 
     Was ist ein Modul? Was ist ein Interface? Was ist der Unterschied 
     zwischen einem PACKET und einer Prozedur? 
     Die Realisierung von WIDERSTAND durch INTs ist nicht in allen Fällen 
     befriedigend. Warum? (Beachte besonders die Realisierung des OP 
     PARALLEL). 
     Wenn man bei der Typ-Vereinbarung von WIDERSTAND INT gegen REAL 
     austauschen würde, wo müßte man noch Änderungen vornehmen? Sind 
     insbesondere Änderungen im Benutzer-Programm (hier im 'main packet') 
     notwendig? 
Übungsziel: PACKET-Begriff 
 
 
 
Programmstruktur 2 
 
Nun können wir auch erklären, wie ein ELAN-Programm mit mehreren PACKETs 
aussieht. Ein Programm besteht aus einer Folge von PACKETs, dem ein 'main 
packet' folgt. Es ist auch möglich, PACKETs vorübersetzen zu lassen, so daß 
es für einen Nutzer so aussieht, als ob er nur ein 'main packet' übersetzen 
läßt. Tatsächlich sind zumindest die Standard-Packets vorübersetzt vorhanden.