Stampa le dimensioni degli intervalli all'interno di un brano musicale


10

sfondo

Nella musica occidentale, ogni singola nota musicale ha un nome assegnato. All'interno di ogni ottava, ci sono dodici note uniche nel seguente ordine: "CC # / Db DD # / Eb EFF # / Gb GG # / Ab AA # / Bb B C", dove la C finale è un'ottava sopra la prima.

Per dire la differenza tra note di ottave diverse, un numero (per questa sfida limitato a una singola cifra) viene aggiunto alla fine del nome della nota. Pertanto, C5 è la nota che è un'ottava sopra C4. Bb6 è sopra B5.

Un fatto importante è che B5 e C6 sono note che si trovano una accanto all'altra e che C0 e B9 sono le note più basse e più alte.

Tra due note qualsiasi, c'è una distanza che è il numero di semitoni tra loro. Bb4 è un semitono al di sotto di B4, che è esso stesso un semitono al di sotto di C5. Ci sono dodici semitoni in un'ottava, quindi Bb4 è una distanza di 12 da A # 3 poiché è un'ottava sopra di essa (nota come una singola nota può avere fino a due nomi).

La sfida

La tua sfida è quella di scrivere il programma più breve possibile che può prendere un elenco di note musicali da STDIN e stampare l'elenco delle modifiche agli intervalli su STDOUT.

L'input sarà un elenco separato da spazi di note musicali. Ogni nota sarà composta da una lettera maiuscola AG, un segno b o # opzionale e un numero a una cifra. Non dovrai avere a che fare con E # / Fb o B # / Cb. Esempio di input:

C4 D4 E4 F4 G4 A4 B4 C5 C4

L'output sarà un elenco di numeri interi separati da spazio che rappresentano la distanza tra ogni nota successiva, sempre preceduta da un + o - per mostrare se la nota era crescente o decrescente rispetto a quella precedente. Verrà sempre emesso un numero in meno rispetto alle note immesse. Esempio di output per l'input sopra:

+2 +2 +1 +2 +2 +2 +1 -12

Alcuni altri input di esempio:

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

E i loro corrispondenti output:

-1 +1 -5 +5 +2 -2 -5
+11 +11 +11 +11 +11 +11 +11
+1 -2 +3 -4 +5

Regole e restrizioni

  1. Il vincitore è determinato dal numero di caratteri nel codice sorgente

  2. Il programma dovrebbe essere composto solo da caratteri ASCII stampabili

  3. Non è consentito utilizzare alcun tipo di funzione integrata correlata alla musica o al suono

  4. Oltre a ciò, si applicano le regole standard per il golf


Dovrebbe stampare +0o -0o 0per due note identiche?
Howard,

@Howard Dato che non ho specificato nessuno dei due è accettabile.
PhiNotPi

1
"Bb4 è un semitono sotto B4, che è esso stesso un semitono sotto C4". Intendi C5 alla fine, giusto?
Keith Randall,

Caspita, non l'ho mai notato. Grazie per aver individuato l'errore. Ora è riparato.
PhiNotPi

Risposte:


6

GolfScript, 61

" "/{)12*\{"bC#D EF G A B"?(+}/}%(\{.@-.`\0>{"+"\+}*\}/;]" "*

4

Haskell, 161 caratteri

f(c:r)=maybe(12*read[c])(+f r).lookup c$zip"bC#D.EF.G.A.B"[-1..]
g n|n<0=show n|1<3='+':show n
h x=zipWith(-)(tail x)x
main=interact$unwords.map g.h.map f.words

4

Perl, 103

#!/usr/bin/perl -an
/.((b)|(\D))?/,(($~,$,)=($,,12*$'+ord()%20%7*2+(ord()%7>3)-$-[2]+$-[3]))[0]&&printf'%+d ',$,-$~for@F

3

C, 123 caratteri

Basato sulla soluzione di leftaroundabout, con alcuni miglioramenti.

main(c,b,d)
    char*b;
{
    while(d=c,~scanf("%s",b)?c=-~*b*1.6,c%=12,c+=b[~b[1]&16?c+=1-b[1]/40,2:1]*12:0)
        d>1&&printf("%+d ",c-d);
}

Alcuni trucchi che ritengo siano degni di nota:
1. argv[0](qui chiamato b) è un puntatore al nome del programma, ma qui usato come buffer di memoria virtuale. Abbiamo bisogno solo di 4 byte (ad es. C#2\0), Quindi ne abbiamo abbastanza.
2. cè il numero di argomenti, quindi inizia come 1 (se eseguito senza argomenti). Lo usiamo per impedire la stampa al primo turno.

Possibile problema - c+=b[..c+=..]è un po 'strano. Non penso che sia un comportamento indefinito, perché ?:è un punto di sequenza, ma forse mi sbaglio.


Se lo pensi come c = c + b[..c+=..], allora è un comportamento abbastanza chiaramente indefinito. Indipendentemente dal sequenziamento all'interno [..], non sai se l'esterno cviene recuperato prima, durante o dopo b[..].
effimero

@ephemient, immagino che in teoria un compilatore potrebbe fare REG=c;REG+=b[..c+=..];c=REG. Tuttavia, sarò sorpreso di vedere qualcosa di simile in pratica. Ma è ancora UB.
ugoren,

È Code Golf - abbiamo già invocato UB usando scanfsenza un prototipo, e va bene. È bello sapere cosa è e non è legale nella vita reale :)
effimero

2

C, 241 229 183

F(c){c-=65;return c*1.6+sin(c/5.+.3)+9;}
#define r if(scanf("%s",b)>0){c=F(*b)%12;c+=b[b[1]<36&&++c||b[1]>97&&c--?2:1]*12
main(e,c,d){char b[4];r;}s:d=c;r;printf("%+d ",c-d);goto s;}}

Invece di fare tu stesso il segno più, puoi semplicemente farlo printf("%+d ",c-d).
Hammar

Puoi omettere include ideone.com/G00fS
Hauleth

Molto bella. Alcuni suggerimenti: F(*b-65)invece di c-=65;, b[1]<36&&++c||b[1]>97&&c--?2:1-> b[1]&16?1:(c+=b[1]%2*2-1,2), abuso di argv da: main(e,b,c,d)char*b{(Usa il puntatore del primo argomento come buffer di lavoro).
ugoren

Un altro - penso che c=F(*b)%12possa essere sostituito con c=-~*b*1.6;c%=12. Perché? sinnell'originale Fpuò essere sostituito con 9.6. c*1.6+9.6è (c+6)*1.6, c-=65e (c+6)diventa c-59, e quindi c+1(60 * 96% 12 == 0).
ugoren,

Grazie per tutti i suggerimenti! Funzionano bene e lo rendono più corto, ma penso che lo lascerò com'è adesso; non sarebbe più la mia soluzione senza il seno.
cessò di girare in senso antiorario l'

1

Fattore, 303 caratteri

USING: combinators formatting io kernel math regexp sequences ;
f contents R/ [#-b]+/ all-matching-slices
[ 0 swap [ {
{ [ dup 48 < ] [ drop 1 ] }
{ [ dup 65 < ] [ 48 - 12 * ] }
{ [ dup 98 < ] [ "C-D-EF-G-A-B" index ] }
[ drop -1 ]
} cond + ] each
swap [ over [ - "%+d " printf ] dip ] when* ] each drop

Con commenti,

! combinators => cond
! formatting => printf
! io => contents
! kernel => swap dup drop over dip when*
! math => < - * +
! regexp => R/ all-matching-slices
! sequences => each
USING: combinators formatting io kernel math regexp sequences ;

f       ! Push false, no previous note value.

! Find notes (as string slices) in standard input. The golfed regexp
! R/ [#-b]+/ matches all notes and no whitespace.
contents R/ [#-b]+/ all-matching-slices

! For each string slice:
[
    0       ! Push 0, initial note value.
    swap    ! Move note slice to top of stack, above note value.

    ! For each Unicode codepoint in note:
    [
        ! Convert the Unicode codepoint to its value in semitones.
        ! For golf, [ 48 ] is shorter than [ CHAR: A ].
        {
            ! Sharp # {35} has 1 semitone.
            { [ dup 48 < ] [ drop 1 ] }
            ! 0-9 {48-57} has 0 to 9 octaves (1 octave = 12 semitones).
            { [ dup 65 < ] [ 48 - 12 * ] }
            ! A-G {65-71} has 0 to 11 semitones.
            { [ dup 98 < ] [ "C-D-EF-G-A-B" index ] }
            ! Flat b {98} has -1 semitone.
            [ drop -1 ]
        } cond

        +       ! Add semitones to cumulative note value.
    ] each

    swap    ! Move previous note value to top of stack.
    ! When there is a previous note value:
    [
        ! Keep current note on stack.
        over [
            ! Compute and print interval.
            - "%+d " printf
        ] dip
    ] when*
    ! Current note replaces previous note at top of stack.
] each

drop    ! Drop previous note, so stack is empty.

Per questo script, un "elenco separato da spazi" può avere 1 o più spazi tra gli elementi e 0 o più spazi all'inizio o alla fine. Questo script stampa uno spazio extra alla fine dell'output, ma accetta anche uno spazio extra (o newline) alla fine dell'input.

Se avrei adottato una definizione più rigorosa, in cui un "elenco separato da spazi" ha esattamente 1 spazio tra gli elementi, e 0 spazi all'inizio o alla fine, allora posso accorciare contents R/ [#-b]+/ all-matching-slicesa contents " " split(usando splitting, non regexp). Tuttavia, avrei bisogno di aggiungere più codice per evitare lo spazio extra alla fine dell'output.

Se uso la parola deprecato dupd, posso ridurre over [ - "%+d " printf ] dipa dupd - "%+d " printf, risparmiando 8 caratteri. Non sto usando parole deprecate perché "devono essere rimosse presto".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.