音楽の内部の間隔のサイズを印刷する


10

バックグラウンド

洋楽では、すべての音符に名前が割り当てられています。各オクターブ内には、「CC#/ Db DD#/ Eb EFF#/ Gb GG#/ Ab AA#/ Bb B C」の順序で12の一意のノートがあり、最後のCは最初の1オクターブ上にあります。

異なるオクターブの音符の違いを伝えるために、数字(このチャレンジでは1桁に制限されています)が音名の最後に追加されます。したがって、C5はC4より1オクターブ高い音です。Bb6はB5の上にあります。

重要な事実は、B5とC6が隣り合っているノートであり、C0とB9が最低と最高のノートであることです。

2つの音符の間には、それらの間の半音の数である距離があります。Bb4はB4の1半音下にあり、それ自体がC5の1半音下にあります。1オクターブには12の半音があるため、Bb4はA#3から12の距離にあります。

チャレンジ

あなたの課題は、STDINから音符のリストを取得し、間隔の変更のリストをSTDOUTに出力できる最短のプログラムを作成することです。

入力は、スペースで区切られた音符のリストです。各メモは、大文字のAG、オプションのbまたは#記号、および1桁の数字で構成されます。E#/ FbまたはB#/ Cbを処理する必要はありません。入力例:

C4 D4 E4 F4 G4 A4 B4 C5 C4

出力は、連続する各ノート間の距離を表す整数のスペースで区切られたリストになります。常に前に+または-が付いて、ノートが前のノートと比較して昇順か降順かを示します。入力されたノートよりも常に1つ少ない数が出力されます。上記の入力の出力例:

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

さらにいくつかの入力例:

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

そして対応する出力:

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

ルールと制限

  1. 勝者はソースコードの文字数によって決まります

  2. プログラムは、印刷可能なASCII文字のみで構成する必要があります

  3. 音楽やサウンドに関連するあらゆる組み込み機能の使用は許可されていません

  4. それ以外には、標準のコードゴルフ規則が適用されます


2つの同一のノートを印刷する+0-0、またはそれとも印刷する必要がありますか0
ハワード

@ハワード指定しなかったのでどちらでもかまいません。
PhiNotPi

1
「Bb4はB4の1半音で、それ自体はC4の1半音です。」最後のC5のことですか?
キースランダル

うわー、それに気づかなかった。エラーを見つけてくれてありがとう。現在は修正されています。
PhiNotPi

回答:



4

Haskell、161文字

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

4

Perl、103

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

3

C、123文字

leftaroundaboutのソリューションに基づいて、いくつかの改善を加えました。

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

私が言及する価値があると思ういくつかのトリック:
1. argv[0](ここではと呼ばれますb)はプログラム名へのポインターですが、ここではスクラッチバッファーとして使用されます。必要なのは4バイト(例:)だけなC#2\0ので、十分です。
2. cは引数の数なので、1から始まります(引数なしで実行した場合)。初回の印刷防止に使用します。

考えられる問題- c+=b[..c+=..]ちょっと奇妙です。?:シーケンスポイントであるため、これは未定義の動作ではないと思いますが、おそらく私は間違っています。


あなたがそれをと考えるならc = c + b[..c+=..]、それはかなり明らかに未定義の振る舞いです。内の順序付けに関係なく[..]、アウターcがの前、最中、または後にフェッチされたかどうかはわかりませんb[..]
ephemient

@ephemient、私は理論的にはコンパイラができると思いますREG=c;REG+=b[..c+=..];c=REG。しかし、実際にこのようなものを見たときは驚きます。しかし、それはまだUBです。
ugoren

それはコードゴルフです- scanfプロトタイプなしでを使用してすでにUBを呼び出しており、それで問題ありません。実生活で何が合法で何が合法でないかを知るのは良いことです:)
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;}}

プラス記号を自分で作成する代わりに、自分で作成することができますprintf("%+d ",c-d)
hammar 2012


非常に素晴らしい。いくつかの提案:、->のF(*b-65)代わりにc-=65;、argvを次のように乱用します(最初の引数ポインターを作業バッファーとして使用します)。b[1]<36&&++c||b[1]>97&&c--?2:1b[1]&16?1:(c+=b[1]%2*2-1,2)main(e,b,c,d)char*b{
ugoren

別のもの- c=F(*b)%12と置き換えることができると思いますc=-~*b*1.6;c%=12。どうして?sinオリジナルでFは9.6に置き換えることができます。c*1.6+9.6(c+6)*1.6c-=65(c+6)なりc-59、その後c+1(60 * 96%12 == 0)になります。
ugoren

すべての提案をありがとう!彼らはうまく機能し、それを短くしますが、私は今のままにしておこうと思います。サインがなければ、それは本当に私の解決策ではありません。
反時計回りを停止

1

ファクター、303文字

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

コメントで、

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

このスクリプトの場合、「スペース区切りリスト」では、要素間に1つ以上のスペース、および先頭または末尾に0個以上のスペースを含めることができます。このスクリプトは、出力の最後に余分なスペースを出力しますが、入力の最後に余分なスペース(または改行)も受け入れます。

「スペースで区切られたリスト」の要素間にスペースが1つだけあり、最初または最後のスペースが0である、より厳密な定義を採用する場合contents R/ [#-b]+/ all-matching-slicescontents " " split(を使用するのsplittingではなくregexp)に短縮できます。ただし、出力の最後の余分なスペースを防ぐために、さらにコードを追加する必要があります。

非推奨の単語を使用するとdupd、に短縮over [ - "%+d " printf ] dipしてdupd - "%+d " printf8文字節約できます。「すぐに削除されることを意図している」ため、廃止された単語は使用していません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.