Hintergrund
In der westlichen Musik hat jede einzelne Musiknote einen zugewiesenen Namen. Innerhalb jeder Oktave gibt es zwölf eindeutige Noten in der folgenden Reihenfolge: "CC # / Db DD # / Eb EFF # / Gb GG # / Ab AA # / Bb B C", wobei das letzte C eine Oktave über dem ersten liegt.
Um den Unterschied zwischen Noten unterschiedlicher Oktaven zu erkennen, wird am Ende des Notennamens eine Zahl (für diese Herausforderung auf eine einzelne Ziffer beschränkt) angehängt. Somit ist C5 die Note, die eine Oktave über C4 liegt. Bb6 liegt über B5.
Eine wichtige Tatsache ist, dass B5 und C6 Noten sind, die direkt nebeneinander liegen, und dass C0 und B9 die niedrigsten und höchsten Noten sind.
Zwischen zwei beliebigen Noten befindet sich ein Abstand, der der Anzahl der Halbtöne zwischen ihnen entspricht. Bb4 ist ein Halbton unter B4, der selbst ein Halbton unter C5 ist. Es gibt zwölf Halbtöne in einer Oktave, also ist Bb4 ein Abstand von 12 von A # 3, da es eine Oktave darüber ist (beachten Sie, dass eine einzelne Note bis zu zwei Namen haben kann).
Die Herausforderung
Ihre Herausforderung besteht darin, ein möglichst kurzes Programm zu schreiben, das eine Liste von Musiknoten von STDIN übernehmen und die Liste der Intervalländerungen in STDOUT drucken kann.
Die Eingabe erfolgt durch Leerzeichen getrennte Liste von Musiknoten. Jede Notiz besteht aus einem Großbuchstaben AG, einem optionalen b- oder # -Zeichen und einer einstelligen Zahl. Sie müssen sich nicht mit E # / Fb oder B # / Cb befassen. Beispieleingabe:
C4 D4 E4 F4 G4 A4 B4 C5 C4
Die Ausgabe ist eine durch Leerzeichen getrennte Liste von Ganzzahlen, die den Abstand zwischen jeder aufeinanderfolgenden Note darstellen, wobei immer ein + oder - vorangestellt wird, um anzuzeigen, ob die Note relativ zu der vorhergehenden Note auf- oder absteigend war. Es wird immer eine Nummer weniger ausgegeben als eingegebene Noten. Beispielausgabe für die obige Eingabe:
+2 +2 +1 +2 +2 +2 +1 -12
Einige weitere Beispieleingaben:
E5 D#5 E5 B4 E5 F#5 E5 B4
C0 B0 Bb1 A2 G#3 G4 F#5 F6
G4 Ab4 Gb4 A4 F4 A#4
Und ihre entsprechenden Ausgaben:
-1 +1 -5 +5 +2 -2 -5
+11 +11 +11 +11 +11 +11 +11
+1 -2 +3 -4 +5
Regeln und Einschränkungen
Der Gewinner wird durch die Anzahl der Zeichen im Quellcode bestimmt
Ihr Programm sollte nur aus druckbaren ASCII-Zeichen bestehen
Sie dürfen keine eingebaute Funktion verwenden, die sich auf Musik oder Sound bezieht
Ansonsten gelten die Standard-Code-Golfregeln
quelle
+0
oder-0
oder0
für zwei identische Notizen?Antworten:
GolfScript, 61
quelle
Haskell, 161 Zeichen
quelle
Perl, 103
quelle
C, 123 Zeichen
Basierend auf der Lösung von leftaroundabout mit einigen Verbesserungen.
Einige Tricks, die meiner Meinung nach erwähnenswert sind:
1.
argv[0]
(hier genanntb
) ist ein Zeiger auf den Programmnamen, wird hier jedoch als Arbeitspuffer verwendet. Wir brauchen nur 4 Bytes (zBC#2\0
), also haben wir genug.2.
c
ist die Anzahl der Argumente, daher beginnt sie mit 1 (wenn sie ohne Argumente ausgeführt wird). Wir verwenden es, um das Drucken in der ersten Runde zu verhindern.Mögliches Problem -
c+=b[..c+=..]
ist irgendwie seltsam. Ich denke nicht, dass es undefiniertes Verhalten ist, weil?:
es ein Sequenzpunkt ist, aber vielleicht irre ich mich.quelle
c = c + b[..c+=..]
, dann ist es ziemlich eindeutig undefiniertes Verhalten. Unabhängig von der Reihenfolge innerhalb[..]
wissen Sie nicht, ob das Äußerec
vorher, während oder nachher abgerufen wirdb[..]
.REG=c;REG+=b[..c+=..];c=REG
. Ich werde jedoch überrascht sein, so etwas in der Praxis zu sehen. Aber es ist immer noch UB.scanf
ohne Prototyp aufgerufen , und das ist in Ordnung. Es ist nur gut zu wissen, was im wirklichen Leben legal ist und was nicht :)C,
241229183quelle
printf("%+d ",c-d)
.F(*b-65)
stattc-=65;
,b[1]<36&&++c||b[1]>97&&c--?2:1
->b[1]&16?1:(c+=b[1]%2*2-1,2)
, Missbrauch argv von:main(e,b,c,d)char*b{
(Verwenden Sie das erste Argument Zeiger als Arbeitspuffer).c=F(*b)%12
kann durch ersetzt werdenc=-~*b*1.6;c%=12
. Warum?sin
im OriginalF
kann durch 9.6 ersetzt werden.c*1.6+9.6
ist(c+6)*1.6
,c-=65
und(c+6)
wurdec-59
, und dannc+1
(60 * 96% 12 == 0).Faktor 303 Zeichen
Mit Kommentaren,
Bei diesem Skript kann eine "durch Leerzeichen getrennte Liste" 1 oder mehr Leerzeichen zwischen Elementen und 0 oder mehr Leerzeichen am Anfang oder Ende enthalten. Dieses Skript druckt zwar ein zusätzliches Leerzeichen am Ende der Ausgabe, akzeptiert jedoch auch ein zusätzliches Leerzeichen (oder eine neue Zeile) am Ende der Eingabe.
Wenn ich eine strengere Definition verwenden würde, bei der eine "durch Leerzeichen getrennte Liste" genau 1 Leerzeichen zwischen Elementen und 0 Leerzeichen am Anfang oder Ende enthält, kann ich
contents R/ [#-b]+/ all-matching-slices
aufcontents " " split
(mitsplitting
, nichtregexp
) verkürzen . Ich müsste jedoch mehr Code hinzufügen, um den zusätzlichen Speicherplatz am Ende der Ausgabe zu verhindern.Wenn ich das veraltete Wort verwende
dupd
, kann ich esover [ - "%+d " printf ] dip
aufdupd - "%+d " printf
8 Zeichen verkürzen . Ich verwende keine veralteten Wörter, weil sie "bald entfernt werden sollen".quelle