Hallo zusammen,
da sich die letzte Code Kata großer Beliebtheit erfreut hat und ich auch außerhalb des Forums positives Feedback bekommen habe, möchte ich für die kommende Woche eine neue Übungsaufgabe stellen.
Hierbei handelt es sich sozusagen um die Mutter aller Code Katas: FizzBuzz
Hier nocheinmal die Aufgabenstellung aus der verlinkten Seite:
Schreibe ein Programm, dass dir die Zahlen von 1 bis 100 ausgibt –
An dieser Stelle spannender gestaltet sich die Frage, wie man eine solche Code Kata in SAS modelliert und an die Aufgabenstellung herantritt.
Eine Möglichkeit besteht darin sich die Ergebnisse in ein Dataset berechnen zu lassen. Um die Verarbeitungslogik zu prüfen, könnte man anschließend die Daten per PROC COMPARE mit den erwarteten Daten vergleichen. Die Vergleichsdaten können Sie sich per Skript erzeugen. Die Kata könnte man daher versuchen testgetrieben zu entwickeln.
Neben der reinen Berechnung kann man allerdings auch seine Fähigkeiten testen, um die Berechnungsfunktionalität möglichst wiederverwendbar in unterschiedlichen SAS Techniken bereitzustellen.
Mir kommen hierzu spontan drei Lösungsansätze in den Sinn:
Zu 3. stelle ich mir das Ergebnis so vor, dass man den Rückgabewert des Makros direkt verwenden kann, als z.B. sollte
data ausgabe;
result = "%FizzBuzz(10)";
run;
direkt vom Makroprozessor in result = "Buzz"; aufgelöst werden.
Alle 3 Varianten habe ich bereits implementiert und werde an dieser Stelle kommenden Freitag meine Lösungen vorstellen.
Den Schwierigkeitsgrad dieser recht simplen Aufgabe kann jeder für sich selbst bestimmen.
Am einfachsten ist natürlich eine Lösung direkt innerhalb eines Datasteps, die nur die Daten erzeugt.
Schwieriger wird die Makrolösung.
Wer besonders viel auf seine Programmierfähigkeiten hält, versucht sich an der FCMP- und Format-Lösung.
Bis dahin viel Vergnügen beim Tüfteln und selbst ausprobieren auf welche Art und Weise sich das Problem in SAS lösen lässt.
Viele Grüße,
Andreas Menrath
Mit diesem Skript lassen sich die Vergleichsdaten erzeugen:
data comp;
length expected_result $8;
input expected_result $;
datalines;
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
Fizz
22
23
Fizz
Buzz
26
Fizz
28
29
FizzBuzz
31
32
Fizz
34
Buzz
Fizz
37
38
Fizz
Buzz
41
Fizz
43
44
FizzBuzz
46
47
Fizz
49
Buzz
Fizz
52
53
Fizz
Buzz
56
Fizz
58
59
FizzBuzz
61
62
Fizz
64
Buzz
Fizz
67
68
Fizz
Buzz
71
Fizz
73
74
FizzBuzz
76
77
Fizz
79
Buzz
Fizz
82
83
Fizz
Buzz
86
Fizz
88
89
FizzBuzz
91
92
Fizz
94
Buzz
Fizz
97
98
Fizz
Buzz
;
run;
Tolle Idee!
Endlich mal eine Gelgenheit, PROC FCMP auszuprobieren...
Letztendlich habe ich es auch mit einem Format gelöst.
Schönen Abend!
proc fcmp outlib=WORK.FUNKTIONEN.FIZZBUZZ;
function FIZZBUZZ(I) $;
DREI = mod(I,3);
FUENF = mod(I,5);
select;
when(DREI eq 0 and FUENF eq 0) return("FizzBuzz");
when(DREI eq 0 and FUENF ne 0) return("Fizz ");
when(DREI ne 0 and FUENF eq 0) return("Buzz ");
otherwise return(put(I,8.));
end;
endsub;
run;
options cmplib=(work.FUNKTIONEN);
proc format;
value FIZZBUZZ
other=[FIZZBUZZ()];
run;
data FIZZBUZZ;
attrib i format=8.;
attrib j format=FIZZBUZZ.;
do i=1 to 100;
j = i;
output;
end;
run;
proc print noobs;run;
Hallo zusammen,
ich möchte nun meine Lösungen an dieser Stelle vorstellen.
Die FCMP und Format-Lösung ist nahezu identisch zur Lösung von Hr. Kothenschulte:
proc fcmp outlib=work.functions.codekata;
function fizzbuzz(zahl) $;
if(mod(zahl,3*5) = 0)
then return("FizzBuzz");
if(mod(zahl,3) = 0)
then return("Fizz");
if(mod(zahl,5) = 0)
then return("Buzz");
return(strip(put(zahl, 8.)));
endsub;
run;
options cmplib=work.functions;
proc format;
value fizzbuzz other=[fizzbuzz()];
run;
data fbdata;
attrib fbvar
length=8
format=fizzbuzz.
label="numeric value with cool custom FizzBuzz format" ;
do fbvar=1 to 100;
output;
end;
run;
Durch das Format auf den Daten bekommt man auch eine schöne Ausgabe z.B. im Enterprise Guide:
Die Makro-Lösung war auch schnell erstellt:
%macro FizzBuzz(num);
%local result;
%if (%sysfunc(mod(&num,3*5)) = 0) %then %do;
%let result = FizzBuzz;
%end;
%else %if (%sysfunc(mod(&num,3)) = 0) %then %do;
%let result = Fizz;
%end;
%else %if (%sysfunc(mod(&num,5)) = 0) %then %do;
%let result = Buzz;
%end;
%else %do;
%let result = #
%end;
%* return result;
&result.
%mend FizzBuzz;
Man kann das Makro nun vielseitig einsetzbar in unterschiedlichen Szenarien einsetzen, z.B. :
%put result 5=%FizzBuzz(5);
%put result 3=%FizzBuzz(3);
%put result 15=%FizzBuzz(15);
%put result 2=%FizzBuzz(2);
data ausgabe;
result = "%FizzBuzz(10)";
run;
%macro print();
%do i=1 %to 100;
%put %FizzBuzz(&i);
%end;
%mend print;
%print;
Ich persönlich finde jedoch die FCMP Lösung besser und eleganter als die Makro-Umsetzung.
Zuletzt habe ich das Ganze auch noch als DataStep 2 Programm implementiert:
proc ds2;
data FizzBuzz(overwrite=yes);
dcl int number;
dcl char(8) formatted;
method fizzBuzz(int zahl) returns varchar(8);
if(mod(zahl,3*5) = 0)
then return('FizzBuzz');
if(mod(zahl,3) = 0)
then return('Fizz');
if(mod(zahl,5) = 0)
then return('Buzz');
return(strip(put(zahl, 8.)));
end;
method init();
do number = 1 to 100;
formatted = fizzBuzz(number);
output;
end;
end;
enddata;
run;
quit;
Das Programmieren mit DS2 macht allerdings unter SAS 9.4 M2 wirklich keinen Spaß. Man muss zwar etwas umdenken im Vergleich zum klassischen Datastep, aber das ist nicht das Problem.
Vielmehr vermisse ich Möglichkeiten zum Debugging meines DS2-Codes. An vielen Stellen erhält der Anwender noch nicht einmal eine Fehlermeldung an welcher Stelle ein Problem auftritt. Stattdessen gibt es nur einen generischen Fehler:
Das Programm war jetzt noch sehr überschaubar. Wie aufwändig die Fehlersuche bei größeren DS2-Codeblöcken wird, will ich mir an dieser Stelle gar nicht erst vorstellen...
Hoffentlich bessert SAS an dieser Stelle mit M3 und Folgeversionen noch nach!
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!