Imprimir os tamanhos dos intervalos dentro de uma peça musical

10

fundo

Na música ocidental, cada nota musical possui um nome atribuído. Dentro de cada oitava, há doze notas exclusivas na seguinte ordem: "CC # / Db DD # / Eb EFF # / Gb GG # / Ab AA # / Bb B C", onde o C final é uma oitava acima da primeira.

Para saber a diferença entre notas de oitavas diferentes, um número (para esse desafio restrito a um único dígito) é anexado ao final do nome da nota. Assim, C5 é a nota que está uma oitava acima de C4. Bb6 está acima de B5.

Um fato importante é que B5 e C6 são notas próximas umas das outras e que C0 e B9 são as notas mais baixa e mais alta.

Entre duas notas, existe uma distância que é o número de semitons entre elas. Bb4 é um semitom abaixo de B4, que é ele próprio um semitom abaixo de C5. Há doze semitons em uma oitava, então Bb4 está a uma distância de 12 de A # 3, pois está uma oitava acima dela (observe como uma única nota pode ter até dois nomes).

O desafio

Seu desafio é escrever o programa mais curto possível que possa obter uma lista de notas musicais de STDIN e imprimir a lista de alterações de intervalo em STDOUT.

A entrada será uma lista separada por espaços de notas musicais. Cada nota consistirá em uma letra maiúscula AG, um sinal b ou # opcional e um número de um dígito. Você não precisará lidar com E # / Fb ou B # / Cb. Exemplo de entrada:

C4 D4 E4 F4 G4 A4 B4 C5 C4

A saída será uma lista de números inteiros separados por espaço, que representa a distância entre cada nota sucessiva, sempre prefixada com + ou - para mostrar se a nota estava subindo ou descendo em relação à anterior. Sempre haverá um número a menos emitido do que as notas inseridas. Exemplo de saída para a entrada acima:

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

Mais alguns exemplos de entradas:

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 suas saídas correspondentes:

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

Regras e Restrições

  1. O vencedor é determinado pelo número de caracteres no código fonte

  2. Seu programa deve consistir apenas em caracteres ASCII imprimíveis

  3. Você não tem permissão para usar qualquer tipo de função interna relacionada a música ou som

  4. Fora isso, aplicam-se regras de código padrão para golfe

PhiNotPi
fonte
Deveria imprimir +0ou -0ou 0para duas notas idênticas?
274 Howard
@ Howard Desde que eu não especifiquei, qualquer um é aceitável.
PhiNotPi
11
"Bb4 é um semitom abaixo de B4, que é ele próprio um semitom abaixo de C4". Você quer dizer C5 no final disso, certo?
Keith Randall
Uau, nunca notei isso. Obrigado por detectar o erro. Está consertado agora.
PhiNotPi 08/04/12

Respostas:

6

GolfScript, 61

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

Haskell, 161 caracteres

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
hammar
fonte
4

Perl, 103

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

C, 123 caracteres

Com base na solução da esquerda, com algumas melhorias.

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);
}

Alguns truques que eu acho que valem a pena mencionar:
1. argv[0](aqui chamado b) é um ponteiro para o nome do programa, mas usado aqui como um buffer temporário. Nós precisamos apenas de 4 bytes (por exemplo C#2\0), então temos o suficiente.
2. cé o número de argumentos e, portanto, começa como 1 (quando executado sem argumentos). Nós o usamos para impedir a impressão na primeira rodada.

Possível problema - c+=b[..c+=..]é meio estranho. Eu não acho que seja um comportamento indefinido, porque ?:é um ponto de sequência, mas talvez eu esteja errado.

Ugoren
fonte
Se você pensa assim c = c + b[..c+=..], é um comportamento claramente indefinido. Independentemente do seqüenciamento interno [..], você não sabe se o externo cé buscado antes, durante ou depois b[..].
ephemient
@hemient, eu acho que em teoria um compilador poderia fazer REG=c;REG+=b[..c+=..];c=REG. No entanto, ficarei surpreso ao ver algo assim na prática. Mas ainda é UB.
Ugoren
É o Code Golf - já invocamos o UB usando scanfsem protótipo, e tudo bem. É apenas bom saber o que é e não é legal na vida real :)
ephemient
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;}}
deixou de girar contra-relógio
fonte
Em vez de fazer o sinal de mais, você pode fazer printf("%+d ",c-d).
Hammar
Você pode omitir inclui ideone.com/G00fS
Hauleth
Muito agradável. Algumas sugestões: em F(*b-65)vez de c-=65;, b[1]<36&&++c||b[1]>97&&c--?2:1-> b[1]&16?1:(c+=b[1]%2*2-1,2), abusam main(e,b,c,d)char*b{do argumento por: (Use o ponteiro do primeiro argumento como um buffer de trabalho).
ugoren
Outro - acho que c=F(*b)%12pode ser substituído por c=-~*b*1.6;c%=12. Por quê? sinno original Fpode ser substituído por 9.6. c*1.6+9.6é (c+6)*1.6, c-=65e (c+6)se torna c-59, e então c+1(60 * 96% 12 == 0).
ugoren
Obrigado por todas as sugestões! Eles funcionam bem e diminuem, mas acho que vou deixar como está agora; realmente não seria mais a minha solução sem o seno.
deixou de girar
1

Fator, 303 caracteres

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

Com comentários,

! 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.

Para esse script, uma "lista separada por espaços" pode ter 1 ou mais espaços entre elementos e 0 ou mais espaços no início ou no final. Esse script imprime um espaço extra no final da saída, mas também aceita um espaço extra (ou nova linha) no final da entrada.

Se eu adotasse uma definição mais rígida, em que uma "lista separada por espaço" tem exatamente 1 espaço entre os elementos e 0 espaços no início ou no final, então posso abreviá-lo contents R/ [#-b]+/ all-matching-slicespara contents " " split(using splitting, notregexp ). No entanto, eu precisaria adicionar mais código para evitar o espaço extra no final da saída.

Se eu usar a palavra descontinuada dupd, posso encurtar over [ - "%+d " printf ] dippara dupd - "%+d " printf, salvando 8 caracteres. Não estou usando palavras obsoletas porque elas "devem ser removidas em breve".

Kernigh
fonte