Interprete di spartiti musicali


11

Data una partitura musicale ASCII, devi essere in grado di emettere la nota e la sua lunghezza corrispondente. Il punteggio conterrà tra le 5 e le 15 note incluse ed è trascritto su un pentagramma. Un pentagramma è composto da cinque linee orizzontali composte da - (meno) caratteri separati da linee di spazi. La linea di fondo nel pentagramma è equivalente alla nota 'E'. La linea di spazi immediatamente sopra la linea di fondo indica una 'F', ed è di un passo più alto che la 'E' sotto di essa. Questo continua come di seguito. Nota che le note vanno solo su "G" prima di ricominciare da "A". Vedi sotto:

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

Si noti che le lettere non sono incluse nell'input. Le note vengono sovrapposte sopra il pentagramma usando il carattere ao (minuscolo ooh) per indicare la 'testa della nota'. Questa testa della nota indica la frequenza della nota e quindi la sua rappresentazione alfabetica come sopra. Ad esempio, una nota posizionata sul punteggio come sotto indica una 'A':

----

----

----
o   
----

----

Una nota, come la "A" sopra, è chiamata "nota intera" e sarebbe suonata per un intero ritmo. Altre durate possono essere indicate includendo una 'radice' che sale dalla nota e tra zero e tre 'bandiere'. Uno stelo è composto da tre | (pipe o barra verticale) impilati immediatamente sopra la testa della nota. Uno stelo senza bandiere è considerato un "quarto di nota" e suona per un quarto di battuta. Le bandiere sono caratteri \ (barra rovesciata) e mano sul lato destro dello stelo. Ogni stelo dimezza il tempo per cui viene suonata la nota. La lunghezza di ciascuna nota sarà una delle seguenti: una nota intera, una nota da un quarto, un'ottava nota, una nota sedicesima o una nota da trenta secondi. Ecco come ogni tipo di nota cercherebbe A:

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

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

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

Mettere insieme più di una nota ti dà un punteggio. Ogni nota può essere considerata larga quattro caratteri, con una nota nella prima colonna di ogni blocco di quattro caratteri. Per esempio :

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

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

L'esempio sopra contiene le seguenti note, in ordine: una nota da un quarto 'G', una nota da trenta secondi 'D', un'ottava nota 'C', un'intera nota 'D' e una nota da sedicesima 'B'. Ogni nota nell'output deve essere nel formato lettera / lunghezza, dove lettera è AG e lunghezza è la frazione della lunghezza della nota rispetto a un'intera nota. Come eccezione, la lunghezza e / carattere non devono essere stampati se la nota è una nota intera. Ogni nota nell'output deve essere separata da un singolo spazio. Pertanto, per il punteggio precedente, il codice dovrebbe generare quanto segue:

G/4 D/32 C/8 D B/16
  • Le note saranno nel seguente intervallo: EFGABCDE F. Nota che è necessario stampare solo la lettera, l'ottava viene ignorata.
  • Si noti che il numero di righe di input varia da 9 a 12, poiché le note con un quarto di tempo o meno sulla riga D o superiore richiederanno più righe per essere mostrate completamente.
  • In questo caso non è presente una mezza nota.

Il codice più corto vince (gli spazi bianchi non contano).

Modifica: risolto errore nella spaziatura in un input.

Alcuni input di esempio:

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

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


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

Uscita: EGBDFFACE


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

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


Perché gli spazi bianchi non contano?
JB

@J: In modo che le persone non si sentano inclini a inviare programmi di una riga senza spazi.
Neil,

1
È convenzionale contare gli spazi bianchi ma non contare le nuove righe che sono lì solo per mantenere l'entrata a una larghezza ragionevole. Lo script utente di George lo fa con alcune lingue (incluso c).
dmckee --- ex gattino moderatore,

2
@Neil bene in questo momento tutto ciò che mi sento incline a presentare è un programma di spazi bianchi.
JB,

@Neil, sì, ma poi ottieni smartasses che scrivono soluzioni davvero verbose, le racchiudono in una serie di spazi bianchi e golf una decodifica-e-exec: codegolf.stackexchange.com/questions/3203/meta-golf-challenge/…
stand dal

Risposte:


6

Javascript, 284.279.278.225.221 , 220 caratteri (incluso lo spazio necessario)

One-liner ( test violino ):

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

Leggibile ( violino di prova ):

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

1
Rimuovendo i messaggi inutili ;e facendo alcuni trucchi, puoi renderlo ancora più breve. 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 caratteri)
JiminP

4

Perl, 103 caratteri

(108 se si contano i caratteri spazi necessari)

$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 spazi bianchi per la presentazione:

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

Si noti che suppongo che tutte le righe abbiano la stessa lunghezza (come per la versione rivista della domanda).

Versione riorganizzata con spiegazioni:

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

(soluzione vecchia, più lunga, conservata perché potrebbe essere interessante anche se è un po 'più lunga)

Perl, 147 126 caratteri

( 149 131 se conti gli spazi necessari)

$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 spazi bianchi per la presentazione:

$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

Riorganizzato un po 'per non abusare così tanto della lingua:

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

Si noti che suppongo che tutte le linee abbiano la stessa lunghezza. Se si desidera consentire linee più brevi, una soluzione ovvia è aggiungere $_.=1x$c,all'inizio del programma, al costo di 9 caratteri.

Ho pensato a un altro approccio per evitare parole lunghe come splite maplasciare che gli spazi facessero di più del lavoro, ma la piastra di caldaia e la punteggiatura si sono vendicate, e posso solo arrivare a 130 (144 con spazi necessari).

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

Questa volta la patch per far fronte a linee non finite è un po 'più strana (cosa, hai pensato che non potesse diventare più strana?). 139 caratteri, 155 con spazi bianchi necessari.

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

2

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

Se una funzione è tutto ciò che serve:

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 è richiesto un programma 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)}

Ci sono spazi vuoti tra le barre fino alla fine del punteggio, anche se non ne ho parlato, quindi il programma dovrebbe funzionare indipendentemente. Se la linea con spazi vuoti termina bruscamente, significa comunque che non c'è più input da considerare per quella linea. Non deve semplicemente andare in crash .. :)
Neil,

2

J - 108 caratteri

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

2

Python golf, 207 caratteri.

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

Ho il codice di avvio di golf con Python per 2 giorni e ho trovato che le cose come import sys, sys.stdin.read, sys.stdout.writesono espansiva.


Se non conosci il golf in Python, potresti trovare utile questa domanda sui suggerimenti per il gofling in Python .
Gareth,
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.