Intérprete de partituras musicales

11

Dada una partitura musical ASCII, debe poder emitir la nota y su longitud correspondiente. El puntaje contendrá entre 5 y 15 notas inclusive, y se transcribe en un pentagrama. Un pentagrama se compone de cinco líneas horizontales que comprenden - (menos) caracteres separados por líneas de espacios. La línea de fondo en el pentagrama es equivalente a la nota 'E'. La línea de espacios inmediatamente arriba de la línea inferior indica una 'F', y es de un tono más alto que la 'E' debajo de ella. Esto continúa como a continuación. Tenga en cuenta que las notas solo suben a 'G' antes de comenzar nuevamente en 'A'. Vea abajo:

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

Tenga en cuenta que las letras no están incluidas en la entrada. Las notas se superponen en la parte superior del pentagrama utilizando un carácter ao (minúscula ooh) para indicar la "cabeza de la nota". Este encabezado de nota indica la frecuencia de la nota y, por lo tanto, la representación alfabética de la misma como se indicó anteriormente. Por ejemplo, una nota colocada en el puntaje como se indica a continuación indica una 'A':

----

----

----
o   
----

----

Una nota, como la 'A' anterior, se llama 'nota completa' y se tocaría durante un tiempo completo. Se pueden indicar otras duraciones al incluir un 'vástago' que se eleva desde la nota y entre cero y tres 'banderas'. Un tallo se compone de tres | (tubería o barra vertical) caracteres apilados inmediatamente encima del encabezado de la nota. Un tallo sin banderas se considera un 'cuarto de nota', y juega durante un cuarto de segundo. Las banderas son caracteres \ (barra invertida) y se encuentran en el lado derecho del tallo. Cada vástago reduce a la mitad el tiempo de reproducción de la nota. La duración de cada nota será una de las siguientes: una nota completa, un cuarto de nota, una octava nota, una semicorchea o una nota de treinta segundos. Así se vería cada tipo de nota para A:

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

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

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

Poner más de una nota juntos te da una puntuación. Se puede considerar que cada nota tiene cuatro caracteres de ancho, con una nota en la primera columna de cada bloque de cuatro caracteres. Por ejemplo :

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

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

El ejemplo anterior contiene las siguientes notas, en orden: un cuarto de nota 'G', una nota de treinta segundos 'D', una octava nota 'C', una nota completa 'D' y una decimosexta nota 'B'. Cada nota en su salida debe estar en el formato letra / longitud, donde la letra es AG y la longitud es la fracción de la longitud de la nota en comparación con una nota completa. Como excepción, la longitud y / carácter no deben imprimirse si la nota es una nota completa. Cada nota en su salida debe estar separada por un solo espacio. Por lo tanto, para el puntaje anterior, su código debería generar lo siguiente:

G/4 D/32 C/8 D B/16
  • Las notas estarán en el siguiente rango: EFGABCDE F. Tenga en cuenta que solo se necesita imprimir la letra, se ignora la octava.
  • Tenga en cuenta que el número de líneas de entrada varía de 9 a 12, ya que las notas con un cuarto de hora o menos en la línea D o superior requerirán que se muestren más líneas por completo.
  • No hay media nota en este caso.

El código más corto gana (el espacio en blanco no cuenta).

Editar: corrigió el error en el espaciado en una entrada.

Algunas entradas de muestra:

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

Salida: B / 8 C / 8 D / 8 E / 32 F / 32 G / 32 D


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

Salida: EGBDFFACE


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

Salida: B / 4 A / 8 F / 32 F / 32 EC / 4 B / 32 F / 16

Neil
fuente
¿Por qué no cuenta el espacio en blanco?
JB
@J: para que las personas no se sientan inclinadas a enviar programas de una línea sin espacios.
Neil
1
Es convencional contar espacios en blanco pero no contar nuevas líneas que solo están allí para mantener la entrada con un ancho razonable. El script de usuario de George hace esto con algunos lenguajes (incluido c).
dmckee --- ex-gatito moderador
2
@Neil, bueno, ahora todo lo que me siento inclinado a enviar es un programa de espacios en blanco.
JB
@Neil, sí, pero luego obtienes inteligencia que escriben soluciones realmente detalladas, las empaquetan en una cadena de espacios en blanco y juegan a
stand

Respuestas:

6

Javascript, 284,279,278,225,221 , 220 caracteres (incluido el espacio en blanco necesario)

One-liner ( violín de prueba ):

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

Legible ( violín de prueba ):

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
fuente
1
Al eliminar los correos electrónicos innecesarios ;y hacer algunos trucos, puede hacerlo aún más corto. 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 si cuenta los espacios en blanco necesarios)

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

Con espacios en blanco para la presentación:

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

Tenga en cuenta que supongo que todas las líneas tienen la misma longitud (según la versión revisada de la pregunta).

Versión reorganizada con explicaciones:

#!/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 " ";
    }
}

(solución antigua y más larga, conservada porque puede ser interesante incluso si es un poco más larga)

Perl, 147 126 caracteres

( 149 131 si cuenta los espacios en blanco necesarios)

$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

Con espacios en blanco para la presentación:

$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

Reorganizado un poco para no abusar tanto del 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
    }
}

Tenga en cuenta que supongo que todas las líneas tienen la misma longitud. Si desea permitir líneas más cortas, una solución obvia es agregar $_.=1x$c,al comienzo del programa, a un costo de 9 caracteres.

Pensé en otro enfoque para evitar palabras largas como splity mapy dejar que los espacios hicieran más trabajo, pero la repetitiva y la puntuación se vengaron, y solo puedo reducirlo a 130 (144 con el espacio en blanco necesario).

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

El parche para hacer frente a las líneas sin terminar es un poco más extraño esta vez (¿qué, pensaste que no podría ser más extraño?). 139 caracteres, 155 con espacios en blanco necesarios.

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- deja de ser malvado'
fuente
2

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

Si una función es todo lo que se necesita:

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"")+" "))}

Si se requiere un programa completo:

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
fuente
No hay espacios en blanco entre medio de las rejas hasta el final de la partitura, aunque no lo menciono para que el programa debería funcionar independientemente. Si la línea con espacios en blanco termina abruptamente, significa que no hay más entradas para considerar para esa línea de todos modos. Simplemente no tiene que bloquearse .. :)
Neil
2

J - 108 caracteres

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

Sin golf:

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
fuente
2

Python golf, 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],

He inicio de códigos de golf con Python durante 2 días y me encontré con que cosas como import sys, sys.stdin.read, sys.stdout.writeson expansivas.

Rayo
fuente
Si eres nuevo en el golf en Python, puede que te resulte útil esta pregunta sobre consejos de gofling de Python .
Gareth