..with The Sound of Music. ;)

I had @SoM_notes and $SoM sitting around doing nothing, so this evening, I made them do something. In make_SoM_song and get_SoM_def, you enter a string of alphabetical notes (c, d, e, f, g, a, b). The notes can be separated by a comma, semicolon, or a space. The functions will return the note name given by Maria von Trapp in The Sound of Music.

I wrote random_SoM_note and random_SoM_song because I couldnt' help myself. Most of you know how much I love to randomly generate things. :)

make_SoM_song, get_SoM_def, and random_SoM_song all return array references.

Enjoy the code!

package SoundofMusicSong; use strict; use warnings; use Exporter qw(import); our @EXPORT_OK = qw(make_SoM_song get_SoM_def random_SoM_note random_S +oM_song); my @base_notes = qw(c d e f g a b); my @SoM_notes = qw(do re me fa so la te); my %notes; @notes{@base_notes} = @SoM_notes; my $SoM = { 'do' => 'a deer a female deer', 're' => 'a drop of golden sun', 'me' => 'a name I call myself', 'fa' => 'a long long way to run', 'so' => 'a needle pulling thread', 'la' => 'a note to follow so', 'te' => 'a drink with jam and bread', }; sub make_SoM_song { my ($user_song) = @_; my @song_notes = split(/[ ,;]/, $user_song); my @new_song = map { $_ = $_ =~ /^[a-g]$/ ? $notes{$_} : 'not a note +'; $_ } @song_notes; return \@new_song; } sub get_SoM_def { my ($user_song) = @_; my $notes = make_SoM_song($user_song); my @new_song = map { $_ = $$SoM{$_} ? $_.' '.$$SoM{$_} : 'not a note +'; $_ } @$notes; return \@new_song; } sub random_SoM_note { my $note = $SoM_notes[rand @SoM_notes]; return $note; } sub random_SoM_song { my ($number_of_notes) = @_; my $notes = $number_of_notes ? $number_of_notes : int(rand(100)) + 1 +; my @new_song; push @new_song, random_SoM_note for (1..$notes); return \@new_song; } 1;
No matter how hysterical I get, my problems are not time sensitive. So, relax, have a cookie, and a very nice day!
Lady Aleena

Replies are listed 'Best First'.
Re: The hills are alive...
by Athanasius (Archbishop) on Nov 27, 2014 at 07:26 UTC

    Hello Lady_Aleena,

    This is cool! I just have a couple of comments:

    • my @new_song = map { $_ = $_ =~ /^[a-g]$/ ? $notes{$_} : 'not a note'; + $_ } @song_notes;

      This can be simplified by removing the assignment and the final $_:

      my @new_song = map { /^[a-g]$/ ? $notes{$_} : 'not a note' } @song_not +es;
    • my @new_song = map { $_ = $$SoM{$_} ? $_.' '.$$SoM{$_} : 'not a note'; + $_ } @$notes;

      The final $_ can again be omitted; also, I find the arrow notation clearer than double dollar signs (but that might just be me):

      my @new_song = map { $_ = $SoM->{$_} ? $_ . ' ' . $SoM->{$_} : 'not a +note' } @$notes;

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Athanasius, thank you and both changed to your suggestions.

      No matter how hysterical I get, my problems are not time sensitive. So, relax, have a cookie, and a very nice day!
      Lady Aleena
Re: The hills are alive...
by GrandFather (Saint) on Nov 27, 2014 at 20:59 UTC

    So, lets say we wanted to modify this code a little. First of course we would write unit tests so we have some confidence we don't break anything along the way. The following code could go in an external test script that uses SoundofMusicSong, but for this example just replace the 1; line at the end of the module with:

    sub note_to_sound { my ($note) = @_; return $notes{$note}; } use Test::More tests => 8; return 1 if caller(); # Done if loaded as a module my $song = make_SoM_song (join ' ', 'a' .. 'g'); my @expected = map{note_to_sound($_)} 'a' .. 'g'; Test::More::is_deeply($song, \@expected, "Notes map correctly to phras +es"); my %noteCounts; my $samples = 100000; my $nominal = $samples / 7; ++$noteCounts{random_SoM_note($_)} for 1 .. $samples; Test::More::ok (abs($noteCounts{$_} - $nominal) < 200, "Random distrib +ution of $_: $noteCounts{$_}") for map {note_to_sound($_)} 'a' .. 'g';

    note_to_sound was added to the module to facilitate testing - it avoids having to access %notes directly. Now lets rework the code a little:

    package SoundOfMusicSong; use strict; use warnings; my %SoM = ( 'c' => {phrase => 'a deer a female deer', sound => 'do'}, 'd' => {phrase => 'a drop of golden sun', sound => 're'}, 'e' => {phrase => 'a name I call myself', sound => 'me'}, 'f' => {phrase => 'a long long way to run', sound => 'fa'}, 'g' => {phrase => 'a needle pulling thread', sound => 'so'}, 'a' => {phrase => 'a note to follow so', sound => 'la'}, 'b' => {phrase => 'a drink with jam and bread', sound => 'te'}, ); my @notes = keys %SoM; sub make_SoM_song { my ($user_song) = @_; return [ map {exists $SoM{$_} ? $SoM{$_}->{sound} : 'not a note'} split /\W+/, $user_song ]; } sub getPhrase { my ($user_song) = @_; my $notes = make_SoM_song($user_song); my @new_song = map {$SoM{$_} ? "$_ $SoM{$_}" : 'not a note'} @$not +es; return \@new_song; } sub random_SoM_note { return $SoM{$notes[rand @notes]}{sound}; } sub random_SoM_song { my ($number_of_notes) = @_; $number_of_notes ||= int(rand(100)) + 1; return [map {random_SoM_note()} 1 .. $number_of_notes]; } sub note_to_sound { my ($note) = @_; return $SoM{$note}{sound}; }

    Now we have, for better or worse, a major overhaul of the module, but when we run the unit tests they still all pass so the reworked code is good to go.

    The test suite doesn't test the entire interface provided by the module so the test suite is incomplete and maybe we've introduced badness in the untested areas? If we are really serious about unit testing we would run code coverage tools (see Devel::Cover) in conjunction with the unit tests to ensure all the code we care about is tested.

    Perl is the programming world's equivalent of English
Re: The hills are alive...
by talexb (Chancellor) on Nov 27, 2014 at 21:26 UTC

    Only tangentially related .. my brain, which appears to be a left-brained model, tries very hard to convert numbers into melodies. Case in point, the church in which my chorus rehearses provided us with a storage room with a handle that's locked with a number combination. The secret number is 5164. How to remember that?

    Trying to come up with a mnemonic (51 is 3x17, 64 is 2^6 .. hmm), my brain suggest the opening for 'Doe, a deer', which starts (I think) with the lyric, "When you know the notes to sing .. you can sing 'most anything". The first four notes are So, Do, La, Fa, aka 5, 1, 6, 4. Presto - a mnemonic!

    Well, I thought it was cool.

    Alex / talexb / Toronto

    Thanks PJ. We owe you so much. Groklaw -- RIP -- 2003 to 2013.

Re: The hills are alive...
by karlgoethebier (Abbot) on Nov 28, 2014 at 09:27 UTC