Intérprete de partitura musical

11

Dada uma partitura ascii, você deve ser capaz de produzir a nota e seu comprimento correspondente. A partitura conterá entre 5 e 15 notas, inclusive, e será transcrita em uma pauta. Uma pauta é composta de cinco linhas horizontais que compreendem de - (menos) caracteres separados por linhas de espaços. A linha inferior da pauta é equivalente à nota 'E'. A linha de espaços imediatamente acima da linha inferior indica um 'F' e tem um tom mais alto que o 'E' abaixo dela. Isso continua como abaixo. Observe que as notas só sobem para 'G' antes de começar novamente em 'A'. Ver abaixo:

F ----------
E           
D ----------
C           
B ----------
A           
G ----------
F           
E ----------

Observe que as letras não estão incluídas na entrada. As notas são sobrepostas no topo da pauta, usando o caractere ao (minúsculas ooh) para indicar o 'cabeçalho da nota'. Este cabeçalho da nota indica a frequência da nota e, portanto, a representação alfabética da mesma como acima. Por exemplo, uma nota colocada na partitura como abaixo indica um 'A':

----

----

----
o   
----

----

Uma nota, como o 'A' acima, é chamada de 'nota completa' e seria tocada por uma batida inteira. Outras durações podem ser indicadas pela inclusão de um "caule" subindo da nota e entre zero e três "sinalizadores". Uma haste é composta de três | (barra vertical ou barra vertical) empilhados imediatamente acima da cabeça da nota. Uma haste sem bandeiras é considerada uma 'semínima' e toca por um quarto de batida. Os sinalizadores são caracteres \ (barra invertida) e estão do lado direito da haste. Cada haste reduz pela metade o tempo em que a nota é tocada. O comprimento de cada nota será um dos seguintes: uma nota inteira, semínima, colcheia, semicolcheia ou semagésima segunda. É assim que cada tipo de nota procuraria A:

--------------------

----|---|\--|\--|\--
    |   |   |\  |\
----|---|---|---|\--
o   o   o   o   o
--------------------

--------------------

Juntar mais de uma nota fornece uma pontuação. Cada nota pode ser considerada com quatro caracteres de largura, com uma nota na primeira coluna de cada bloco de quatro caracteres. Por exemplo :

    |\             
----|\--|\----------
    |\  |       |\  
----o---|---o---|\--
|       o       |   
|---------------o---
|                   
o-------------------

--------------------

O exemplo acima contém as seguintes notas, em ordem: uma semínima 'G', uma trigésima segunda nota 'D', uma colcheia 'C', uma nota completa 'D' e uma semicolcheia 'B'. Cada nota na sua saída deve estar no formato letra / comprimento, em que letra é AG e comprimento é a fração do comprimento da nota quando comparada a uma nota inteira. Como exceção, o comprimento e / caractere não devem ser impressos se a nota for uma nota inteira. Cada nota na sua saída deve ser separada por um único espaço. Portanto, para a pontuação acima, seu código deve gerar o seguinte:

G/4 D/32 C/8 D B/16
  • As notas estarão no seguinte intervalo: EFGABCDE F. Observe que apenas a letra precisa ser impressa; a oitava é ignorada.
  • Observe que o número de linhas de entrada varia de 9 a 12, pois as notas com menos de um quarto de tempo na linha D ou superior exigirão que mais linhas sejam exibidas completamente.
  • Não há meia nota neste caso.

O código mais curto vence (o espaço em branco não conta).

Edit: Corrigido erro no espaçamento em uma entrada.

Algumas entradas de amostra:

        |\                    
----|\--|-------------------
|\  |   |                   
|---|---o---------------o---
|   o               |\      
o---------------|\--|\------
            |\  |\  |\      
------------|\--|\--o-------
            |\  o           
------------o---------------

Saída: B / 8 C / 8 D / 8 E / 32 F / 32 G / 32 D


----------------o-------------------
                                o   
------------o-----------------------
                            o       
--------o---------------------------
                        o           
----o-------------------------------
                    o               
o-----------------------------------

Saída: EGBDFFACE


            |\                  
            |\                  
            |\                  
------------o-------|-----------
|               o   |   |\      
|---|\--------------|---|\------
|   |               o   |\      
o---|---|\--------------o---|\--
    o   |\                  |\  
--------|\------------------|---
        o                   o   
--------------------------------

Saída: B / 4 A / 8 F / 32 F / 32 EC / 4 B / 32 F / 16

Neil
fonte
Por que o espaço em branco não conta?
JB
@J: Para que as pessoas não se sintam inclinadas a enviar programas de uma linha sem espaços.
Neil
1
É convencional contar espaços em branco, mas não contar novas linhas que existem apenas para manter a entrada sob uma largura razoável. O script de usuário de George faz isso com alguns idiomas (incluindo c).
dmckee --- gatinho ex-moderador
2
@ Neil bem agora, tudo o que sinto vontade de enviar é um programa em branco.
JB
@ Neil, sim, mas então você obtém espertinhos que escrevem soluções realmente detalhadas, as empacotam em uma série de espaços em branco e jogam
Boothby

Respostas:

6

Javascript, 284,279,278,225,221 , 220 caracteres (incluindo o espaço em branco necessário)

Forro único ( rabeca de teste ):

function a(c){b='',d=c.split('\n');for(e=0;d[0][e++];){f=0;for(i=0;g=d[i++];){h=g[e-1];if(h=='o')b+=(b?' ':'')+String.fromCharCode((d.length+4-i)%7+65);if(h=='|')f=f||4;if(g[e]&&g[e]=='\\')f*=2;}if(f)b+='/'+f;}return b;}

Legível ( rabeca de teste ):

function getNotes(input){
    out='',lines=input.split('\n');

    for(col=0;lines[0][col++];){
        time=0;
        for(i=0;line=lines[i++];){
            char=line[col-1];
            if(char=='o')out+=(out?' ':'')+String.fromCharCode((lines.length+4-i)%7+65);
            if(char=='|')time=time||4;
            if(line[col]&&line[col]=='\\')time*=2;
        }
        if(time)out+='/'+time;
    }
    return out;
}
Briguy37
fonte
1
Ao remover se desnecessários ;e fazer alguns truques, você pode tornar isso ainda mais curto. function a(c){b='',d=c.split('\n');for(e=0;d[0][e++];){for(i=f=0;g=d[i++];){h=g[e-1];if(h=='o')b+=(b?' ':'')+String.fromCharCode((d.length+4-i)%7+65);if(h=='|')f=f||4;f*=1+(g[e]=='\\');}if(f)b+='/'+f}return b}(209 caracteres)
JiminP
4

Perl, 103 caracteres

(108 se você contar os caracteres de espaço em branco necessários)

$i=0,s/\|\\/h /g,map$b[$i++].=$_,/./g for<>;/o/&&print chr 65+(4+length$')%7,/[h|]/&&"/".4*2**y/h//," "for@b

Com espaço em branco para apresentação:

$i=0,
    s/\|\\/h /g,
    map $b[$i++]. = $_, /./g
  for <>;
/o/ && print chr 65 + (4 + length $') % 7,
             /[h|]/ && "/" . 4*2**y/h//,
             " "
  for @b

Observe que suponho que todas as linhas tenham o mesmo comprimento (conforme a versão revisada da pergunta).

Versão reorganizada com explicações:

#!/usr/bin/env perl
# First transpose the list of lines into a list of columns.
my @b = ();               # @b[$i] will contain the characters in column $i
while (<>) {              # for each input line, do
    my $i = 0;            # start in column 0
    s/\|\\/h /g;          # replace '\|' by 'h ', to keep track of part notes in the first column
    foreach (/./g) {      # for each character, do
        $b[$i++] .= $_;   # append the character to the transposed matrix
    }
}
# Now process each column.
foreach (@b) {            # for each column, do
    if (/o/) {            # if it contains a note, then
        print chr(65 + (4 + length $') % 7);    # print the note pitch
        if (/[h|]/) {                           # if this is a part note (had |\ or just |)
            print "/", 4*2**y/h//;              # print /n where n = 2^(subdivision)
        }
        print " ";
    }
}

(solução antiga e mais longa, mantida porque pode ser interessante mesmo que seja um pouco mais)

Perl, 147 126 caracteres

( 149 131 se você contar o espaço em branco necessário)

$c=0,map{/o/?$h[$c]=E:/\\/?$d[$c-1]*=2:/\|/?$d[$c]||=4:++$h[$c];++$c}/./g for<>;print grep{s~$~/$d[$i++] ~;s~/ ~ ~;y/E-M/EFGA-F/}@h

Com espaço em branco para apresentação:

$c = 0,
map { /o/ ? $h[$c]=E :
      /\\/ ? $d[$c-1]*=2 :
      /\|/ ? $d[$c]||=4 :
      ++$h[$c];
      ++$c
    } /./g for <>;
print grep {s~$~/$d[$i++] ~; s~/ ~ ~; y/E-M/EFGA-F/} @h

Reorganizados um pouco para não abusar tanto do idioma:

#!/usr/bin/perl
my @h;          # $h[$c] will contain the note in column $c, if any
my @d;          # $d[$c] will contain the note length (e.g. 4), if any
while (<>) {    # for each input line, do
    my $c = 0;  # column number
    foreach (split //) {   # for each character, do
        if (/o/) { $h[$c] = "E"; }      # o => it's a note; if this is the last line, it's E
        elsif (/\\/) { $d[$c-1] *= 2; } # \ => halve the duration of the note in the previous column
        elsif (/\|/) { $d[$c] ||= 4; }  # | => if this is the first | in the column, we have a quarter note
        else { ++$h[$c]; }              # anything else => bump the note by 1
        ++$c;
     }
}
for (my $i = 0; $i < @h; $i++) { # for each column, do
    $_ = $h[$i];                   # look up the potential note (or garbage if there is no note in this column)
    s~$~/$d[$i++] ~;               # append the duration and a space (or "/ " if there is no duration)
    s~/ ~ ~;                       # remove the spurious "/" if there is no duration
    if (y/E-M/EFGA-F/) {           # if it's a note (i.e. if it contains a letter E-M), then
                                   # fix the letter wraparound and then
        print $_;                    # print the note
    }
}

Note que eu suponho que todas as linhas tenham o mesmo comprimento. Se você deseja permitir linhas mais curtas, uma correção óbvia é adicionar $_.=1x$c,no início do programa, a um custo de 9 caracteres.

Pensei em outra abordagem para evitar palavras longas como splite mape deixar espaços fazer mais do trabalho, mas o clichê e pontuação se vingaram, e só posso obtê-lo para baixo para uma coqueluche 130 (144 com espaços em branco necessário).

sub p{$-[0]}
%a=qw(o $h[p]=E \ $d[&p-1]*=2 | $d[p]||=4 - ++$h[p]);
y/ /-/,s~.~$a{$&}~gee for<>;
print grep{s~$~/$d[$i++] ~;s~/ ~ ~;y/E-M/EFGA-F/}@h

O patch para lidar com linhas inacabadas é um pouco mais estranho dessa vez (o que você achou que não poderia ficar mais estranho?). 139 caracteres, 155 com espaço em branco necessário.

sub p{$-[0]}
%a=qw(o $h[p]=E \ $d[&p-1]*=2 | $d[p]||=4 - ++$h[p]);
$_.=" "x p,y/
 /-/,s~.~$a{$&}~gee for<>;
print grep{s~$~/$d[$i++] ~;s~/ ~ ~;y/E-M/EFGA-F/}@h
Gilles 'SO- parar de ser mau'
fonte
2

Scala (2.9), 352 313 291 294 290 277 274 273 caracteres

Se uma função é tudo o que é necessário:

def m(s:String){var(x,y,z,l)=(0,1,s.count(_=='\n'),Array.fill(99)(0))
var n=l.clone
for(c<-s){if(c=='\n'){x=0;y+=1}
if(c=='\\')l(x-1)+=1
if(c=='|')l(x)+=1
if(c=='o')n(x)="EFGABCDEF"(z-y)
x+=1}
(n,l).zipped.map((x,y)=>if(x>0)print(x.toChar+(if(y>0)"/"+(4<<y-3)else"")+" "))}

Se um programa completo for necessário:

object M extends App{def m(s:String){var(x,y,z,l)=(0,1,s.count(_=='\n'),Array.fill(99)(0))
var n=l.clone
for(c<-s){if(c=='\n'){x=0;y+=1}
if(c=='\\')l(x-1)+=1
if(c=='|')l(x)+=1
if(c=='o')n(x)="EFGABCDEF"(z-y)
x+=1}
(n,l).zipped.map((x,y)=>if(x>0)print(x.toChar+(if(y>0)"/"+(4<<y-3)else"")+" "))}
m(io.Source.stdin.mkString)}
Gareth
fonte
Não são espaços em branco no meio destes bares até o final da pontuação, embora eu não mencioná-lo para que o programa deve funcionar independentemente. Se a linha com espaços em branco terminar abruptamente, significa que não há mais entradas a serem consideradas nessa linha. Ele simplesmente tem de não falhar .. :)
Neil
2

J - 108 caracteres

exit echo}.,>,&.>/_4<@((a.{~32,65+7|4+i.&'o'),(>&0#('/',0":2^]))@((+/@(=&'\'))+2*'|'&e.))@;\|:|.[;._2]stdin''

Ungolfed:

str =: stdin''
lines =: [;._2] str                          NB. split on the last character, the newline
rotated =: |: |. lines                       NB. lines reversed, then transposed
pitch =: 65 + 7 | 4 + i.&'o'                 NB. ord('A') + ( line.index('o') + 4 ) % 7
has_stem =: '|' & e.                         NB. '|' in line?
backslash_count =: (+/ @ (=&'\') )           NB. sum(char = '\\' for char in line)
denom_exp =: backslash_count + 2 * has_stem
fraction =: (>&0 # ('/', 0": 2 ^ ]))         NB. slash + 2^denom_exp, if denom_exp > 0
suffix =: fraction @ denom_exp
note_string =: (a. {~ 32,pitch) , suffix     NB. map(chr, (ord(' '), pitch)) + suffix
boxed_note_string =: < @ note_string @ ;     NB. box the string so it doesn't get padded
each_note_of_the =: boxed_note_string        NB. compute the note for a block of 4 lines
join_to_one_box =: , &. >
exit echo }. , > join_to_one_box / _4 each_note_of_the \ rotated
DCharness
fonte
2

Golfe em Python, 207 caracteres.

import sys
a=[x[:-1]+' '*99 for x in sys.stdin]
for x in range(0,99,4):
 b=''.join((y[x:x+4] for y in a))+'o'
 c=2**(b.count('\\')+('|'in b)*2)
 print'FEDCBAGFE '[b.index('o')/4-len(a)+9]+('','/'+`c`)[c>1],

Eu começar a jogar golfe código com Python por 2 dias e eu achei que coisas como import sys, sys.stdin.read, sys.stdout.writesão expansivas.

Raio
fonte
Se você é novo no golfe em Python, poderá achar útil a pergunta sobre dicas de brincadeiras em python .
Gareth