LesezeichenAbonnierenRSS-Feed abonnieren
AndreasMenrath
Pyrite | Level 9

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 –

  • ist die Zahl durch 3 teilbar, gib das Wort Fizz anstatt der Zahl aus
  • ist die Zahl durch 5 teilbar, so gib das Wort Buzz anstatt der Zahl aus
  • ist die sowohl durch 3 als auch durch 5 teilbar, so gib das Wort FizzBuzz aus.

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:

  1. Stelle die Berechnungslogik als FCMP Funktion bereit
  2. Stelle die Berechnungslogik als SAS Format bereit
  3. Stelle die Berechnungslogik als reine Makrolösung bereit

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

5 ANTWORTEN 5
AndreasMenrath
Pyrite | Level 9

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;

CKothenschulte
Obsidian | Level 7

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;

AndreasMenrath
Pyrite | Level 9

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:

AndreasMenrath
Pyrite | Level 9

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.

AndreasMenrath
Pyrite | Level 9

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: Save the Date

 SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!

Save the date!

Diskussionsstatistiken
  • 5 Antworten
  • 1904 Aufrufe
  • 1 Kudo
  • 2 in Unterhaltung