Cetak ukuran interval di dalam karya musik

10

Latar Belakang

Dalam musik Barat, setiap not tunggal memiliki nama yang ditetapkan. Dalam setiap oktaf, ada dua belas nada unik dalam urutan berikut: "CC # / Db DD # / Eb EFF # / Gb GG # / Ab AA # / Bb B C", di mana C terakhir adalah satu oktaf di atas yang pertama.

Untuk mengetahui perbedaan antara not oktaf yang berbeda, angka (untuk tantangan ini terbatas pada satu digit) ditambahkan ke akhir nama not. Jadi, C5 adalah not yang satu oktaf di atas C4. Bb6 di atas B5.

Fakta penting adalah bahwa B5 dan C6 adalah not yang bersebelahan, dan bahwa C0 dan B9 adalah not terendah dan tertinggi.

Di antara dua not, ada jarak yang merupakan jumlah semitone di antara mereka. Bb4 adalah satu semiton di bawah B4, yang merupakan satu semitone di bawah C5. Ada dua belas semiton dalam satu oktaf, jadi Bb4 adalah jarak 12 dari A # 3 karena itu adalah satu oktaf di atasnya (perhatikan bagaimana satu not dapat memiliki hingga dua nama).

Tantangan

Tantangan Anda adalah menulis program sesingkat mungkin yang dapat mengambil daftar catatan musik dari STDIN dan mencetak daftar perubahan interval ke STDOUT.

Input akan berupa daftar catatan musik yang dipisahkan ruang. Setiap catatan akan terdiri dari huruf besar AG, tanda b atau # opsional, dan satu digit angka. Anda tidak harus berurusan dengan E # / Fb atau B # / Cb. Input contoh:

C4 D4 E4 F4 G4 A4 B4 C5 C4

Output akan berupa daftar bilangan bulat yang dipisahkan oleh spasi yang mewakili jarak antara setiap not berurutan, selalu diawali dengan + atau - untuk menunjukkan apakah not naik atau turun relatif terhadap yang sebelumnya. Akan selalu ada satu nomor lebih sedikit yang dihasilkan dari catatan yang dimasukkan. Contoh output untuk input di atas:

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

Beberapa contoh input lainnya:

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

Dan output yang sesuai:

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

Aturan dan Batasan

  1. Pemenang ditentukan oleh jumlah karakter dalam kode sumber

  2. Program Anda harus terdiri dari hanya karakter ASCII yang dapat dicetak

  3. Anda tidak diizinkan menggunakan fungsi bawaan apa pun yang terkait dengan musik atau suara

  4. Selain itu, aturan golf kode standar berlaku

PhiNotPi
sumber
Haruskah mencetak +0atau -0atau 0dua catatan identik?
Howard
@Howard Karena saya tidak menentukan, salah satunya dapat diterima.
PhiNotPi
1
"Bb4 adalah satu setengah nada di bawah B4, yang itu sendiri satu setengah nada di bawah C4". Maksudmu C5 di akhir itu, kan?
Keith Randall
Wow, tidak pernah memperhatikan itu. Terima kasih telah menemukan kesalahan. Sudah diperbaiki sekarang.
PhiNotPi

Jawaban:

6

GolfScript, 61

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

Haskell, 161 karakter

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
sumber
4

Perl, 103

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

C, 123 karakter

Berdasarkan solusi leftaroundabout, dengan beberapa perbaikan.

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

Beberapa trik yang menurut saya layak disebutkan:
1. argv[0](di sini disebut b) adalah pointer ke nama program, tetapi digunakan di sini sebagai buffer awal. Kami hanya membutuhkan 4 byte (mis. C#2\0), Jadi kami sudah cukup.
2. cadalah jumlah argumen, jadi itu dimulai sebagai 1 (ketika dijalankan tanpa argumen). Kami menggunakannya untuk mencegah pencetakan pada putaran pertama.

Kemungkinan masalah - c+=b[..c+=..]agak aneh. Saya tidak berpikir itu perilaku yang tidak terdefinisi, karena ?:merupakan titik urutan, tapi mungkin saya salah.

ugoren
sumber
Jika Anda menganggapnya sebagai c = c + b[..c+=..], maka itu perilaku yang jelas tidak terdefinisi. Terlepas dari urutan dalam [..], Anda tidak tahu apakah bagian luar cdiambil sebelum, selama, atau setelah b[..].
ephemient
@ephemient, saya kira dalam teori compiler bisa melakukannya REG=c;REG+=b[..c+=..];c=REG. Namun, saya akan terkejut melihat sesuatu seperti ini dalam praktek. Tapi tetap saja UB.
ugoren
Ini Code Golf - kami sudah memanggil UB dengan menggunakan scanftanpa prototipe, dan tidak apa-apa. Hanya baik untuk mengetahui apa yang legal dan tidak legal dalam kehidupan nyata :)
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;}}
berhenti mengubah counterclockwis
sumber
Alih-alih melakukan tanda tambah sendiri, Anda bisa melakukannya printf("%+d ",c-d).
hammar
Anda dapat menghilangkan termasuk ideone.com/G00fS
Hauleth
Sangat bagus. Beberapa saran: F(*b-65)alih-alih c-=65;, b[1]<36&&++c||b[1]>97&&c--?2:1-> b[1]&16?1:(c+=b[1]%2*2-1,2), penyalahgunaan main(e,b,c,d)char*b{argumen oleh: (Gunakan penunjuk argumen pertama sebagai penyangga kantor).
ugoren
Satu lagi - saya pikir c=F(*b)%12bisa diganti dengan c=-~*b*1.6;c%=12. Mengapa? sindi aslinya Fbisa diganti dengan 9.6. c*1.6+9.6adalah (c+6)*1.6, c-=65dan (c+6)menjadi c-59, dan kemudian c+1(60 * 96% 12 == 0).
ugoren
Terima kasih atas semua sarannya! Mereka bekerja dengan baik dan membuatnya lebih pendek, tapi saya pikir saya akan membiarkannya seperti sekarang; itu tidak akan menjadi solusi saya tanpa sinus.
Berhenti menghidupkan counterclockwis
1

Faktor, 303 karakter

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

Dengan komentar,

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

Untuk skrip ini, "daftar yang dipisahkan ruang" dapat memiliki 1 atau lebih spasi di antara elemen, dan 0 atau lebih spasi di awal atau akhir. Skrip ini memang mencetak ruang ekstra di akhir output, tetapi juga menerima ruang tambahan (atau baris baru) di akhir input.

Jika saya akan mengadopsi definisi yang lebih ketat, di mana "daftar yang dipisahkan oleh ruang" memiliki tepat 1 ruang antara elemen, dan 0 ruang di awal atau akhir, maka saya dapat mempersingkat contents R/ [#-b]+/ all-matching-slicesmenjadi contents " " split(menggunakan splitting, bukan regexp). Namun, saya perlu menambahkan lebih banyak kode untuk mencegah ruang ekstra di akhir output.

Jika saya menggunakan kata yang sudah tidak digunakan lagi dupd, saya dapat menyingkat over [ - "%+d " printf ] dipmenjadi dupd - "%+d " printf, menyimpan 8 karakter. Saya tidak menggunakan kata-kata usang karena mereka "dimaksudkan untuk segera dihapus."

kernigh
sumber