Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

runrig (3385)

runrig
  dougwNO@SPAMcpan.org

Just another perl hacker somewhere near Disneyland

I have this homenode [perlmonks.org] of little consequence on Perl Monks [perlmonks.org] that you probably have no interest in whatsoever.

I also have some modules [cpan.org] on CPAN [cpan.org] some of which are marginally [cpan.org] more [cpan.org] useful [cpan.org] than others.

Journal of runrig (3385)

Sunday November 11, 2007
12:10 PM

Free (as in beer) Music Scoring

My daughter is in the school jazz band. She plays french horn. As you might imagine, there isn't much jazz band music written for french horn. The instructor has some trombone music in the same range, but trombone is written in concert pitch with a bass clef, horn is an F instrument written with a treble clef.

I did not want to manually write the notes with pencil and paper, I wanted some sort of "music scoring language" for the computer. At first, I thought of XML (why?), searched for Music and XML, and found a ".org" whose website looked like an advertisement for the company who had come up with this flavor of XML. And the XML was verbose, and I was not looking forward to entering all that cruft.

Then I think I searched for "music scoring opensource", and came up with LilyPond. And I looked at the language, and it was (sometimes) as dense as a perl regular expression. To enter a run of quarter notes, you just enter them "c d e f g". To make them eighth notes, just add the duration to the first one: "c8 d e f g". It gets somewhat ugly when you mark up a note...to make the c staccato and mark it as (dynamic) forte it would be "c8-.\f" (the "f" appears below the note, the "." appears above or below the note depending on stem direction). Dynamics like "f" (in the previous example) and "sfz" are built in, but for "sffz" you have to create a "custom dynamic."

Text markup is ugly too...I wanted to add a "D.S. (w/symbol) al Coda (w/symbol)" above the staff with "(No repeat)" underneath it, and this is what I came up with (it's attached to a rest note):

  r1^\markup { \hspace #3.0 \tiny \center-align {
                 { \line { "D.S. " \musicglyph #"scripts.segno" " al Coda  " \musicglyph #"scripts.coda" } }
                 { "(No Repeat)" }
             } }

You can create macros, and that's what I think I would do next time for the musicglyphs above. Also, glissandos (rising or falling pitch to or from a note) must have starting and ending notes, but sometimes the start or end pitches are indeterminate, so you have to create an "invisible note" and modify the duration of the notes (here the starting note is 9/10 the normal value, so the invisible end note is 1/10 the same value, and the style is a "zigzag" line instead of a straight line):

\once \override Glissando #'style = #'zigzag
  c4*9/10->\glissando
    \hideNotes g4*1/10 \unHideNotes

I had two pieces (each two pages) to transcribe, the first one took me 10 hours or so (big learning curve), the next took less than an hour (it had less text markup). When I was done, I compared the trombone music against the original, corrected my typos(**), then I wrapped the whole thing in a "\transpose f c' { ... }", changed the bass clef to treble, and I had some nice new jazz for horn :-)

Oh yeah, and it has a Scheme based scripting language (which I have not begun to look at), and outputs pdf, postscript (w/direct links to modify the postscript output), and other formats.

(**) Along with the usual typos, before I learned about the hideNotes thing, I had a glissando between a note and a rest, which worked fine for trombone in that instance, but when transposed to horn, it was a glissando up instead of down, so I had to learn all about the hiding notes thing to fix it :-)

Sunday September 23, 2007
02:55 AM

Map first post

While clicking on some YAPC photos on Flickr, I noticed the "(map)" link (I may have been vaguely aware of it in the past, but I actually noticed it this time), and then I was clicking around various photos of Paris (at first YAPC photos, then anything in the neighborhood...BTW I may be visiting next year), then I thought "Hey, why don't I map some of my crappy photos". So I did. I even got first post in a very low density area :-)

I recently tried to make it to the top of the "hill" again (no camera this time though), but it was 90-100 degrees (32-38C), I ran out of water (down to my last pint), and "only" made it to the main divide again. On the map, there are quite a few photos at the peak, but I'd bet that that person drove up...

Thursday September 20, 2007
05:28 PM

Cwd::chdir not useless after all

Prior to today, I always considered Cwd::chdir() rather pointless. It keeps $ENV{PWD} up to date. But if you wanted $ENV{PWD}, why not just call Cwd::cwd() (unless maybe you wanted PWD when the program started, but then you wouldn't need Cwd at all)? Well, we had this awful code:

open( FH, "pwd|" ) or die "....: $!";
my $dir = <FH>;
chomp $dir;

It was awful, but it worked, at least until we migrated this code to a system where '/foo/bar' was a symbolic link to '/foo/blah/bar' (and there was logic that depended on stuff being in '/foo/bar'). pwd in /foo/bar from the command line returned /foo/bar. But pwd from within perl returned /foo/blah/bar. Cwd::cwd() also returned /foo/blah/bar. But $ENV{PWD} returned /foo/bar. And we chdir'd a lot. So Cwd::chdir() saved us from changing some hardcoded /foo/bar's, and gave me an excuse to replace the awful code above with:

my $dir = $ENV{PWD};

And actually, the code is really just trying to recursively search directories, so I really ought to use File::Find...but that's more refactoring than I'm willing to do at this point :-)

Friday September 07, 2007
01:48 PM

WE OWN UR HOMEPAGE

I understand it's their (work's) computer and they can do anything they want to it, but I was using TorgoX's homepage on Firefox (I also copied his Unicode Sliderule locally, and use that in the homepage link), when an update was pushed to my computer from the company servers. After which I noticed that the homepage had changed to the company's intranet main page (which I have as a bookmark, but which I rarely want to see at all, let alone when I start up Firefox). Also, when I reset the home page through the normal Tools->Options, it didn't stick, and would revert when I restarted. I saw a suspicious entry in user.js, changed that, and now all is well. For now.
Thursday August 30, 2007
02:43 PM

I give up -- it's a SCSL

Tired of trying to educate the masses on what kind of language Perl is? I hereby dub Perl and any other language that wants to join the party as a SCSL, which stands for So-Called Scripting Language. If you want to wordify the acronym, I tentatively suggest "scuzzle".

Whenever the word "script" is used in reference to one of these languages, it is implied that what is really meant is "so-called script". It'll even be ok to say "I'm writing a scuzzle" (you don't have to say "I'm writing a scuzz", cuz that just doesn't sound right...though maybe "I'm writing some scuzz"...that sounds better).

You just wait, within 5 years, it'll be common usage. We'll just have to avoid association with the current (informal) definition of scuzzle...or maybe not... .oO( That's one hairy-ass language )...I'm liking this idea more and more :-)

Wednesday August 22, 2007
04:27 PM

XML parser errors

A colleague was developing a PowerCenter transfer with an XML file as the source. She kept getting an error, with a reference to a line number in the file. But there didn't appear to be anything wrong with the indicated line. I ran the file through libxml (via perl), and it gave me a different line number as the error. Then the error was obvious...the encoding claimed to be UTF-8, but there were characters such as ë and Ä in the file. Changing the encoding to ISO-8859-1 seemed to fix it, I'm not sure yet if the supplier of the file will fix it, or if we'll have to fix their tag-soup gunk (there is as yet no perl involved in the process, so I'm not sure if Grant's Rule applies). I went to google to see about any other info with regard to PowerCenter, XML, and line numbers, and not far from the top of the list was my own posts here on use.perl. Now with this post, I may show up even higher :-)
Wednesday July 25, 2007
11:53 AM

More XML Sorting

A few days ago I thought I had it all nailed, sorting the elements, but then I noticed this pattern in the XML (seems like a badly designed schema, oh well):

<a NAME="A">
<b NAME="B">
   <c NAME="FOO"/>
   <d NAME="FOO"/>
   <c NAME="BAR"/>
   <d NAME="BAR"/>
</b>
</a>

In one doc, the FOO's came first, and in the other, the BAR's came first. XML::Filter::Sort didn't handle sorting non-contiguous elements, nor sorting by element name. I briefly looked into patching it to handle that case, but decided against bloating a nice, simple, module API.

I remembered that XSLT could sort, so I started looking into that, and tried the first thing that came up when you search CPAN for XSLT, XML::XSLT. No matter what I tried, though, nothing worked. Then as a last resort I read the fine docs and saw that "sort" was not yet implemented (update: also, it seemed to be applying templates from the bottom up -- see xslt below)

Then I tried XML::Filter::XSLT which was based on XML::LibXSLT (and libxslt), which I had much higher hopes for. I had trouble at first getting it to sort by element name while preserving the attributes (which I could never quite find a full example of), but finally came up with this:

my $sorter = XML::Filter::XSLT->new(Source => {String => <<'EOT'});
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     version="1.0">

<xsl:output method="xml" version="1.0"
encoding="iso-8859-1" indent="yes" omit-xml-declaration="no"/>

<xsl:template match="/">
  <xsl:apply-templates select="@*|node()"/>
</xsl:template>

<xsl:template match="/a/b">
  <xsl:copy>
    <xsl:apply-templates select="@*"/>
    <xsl:apply-templates>
      <xsl:sort select="name()"/>
      <xsl:sort select="@NAME"/>
    </xsl:apply-templates>
  </xsl:copy>
</xsl:template>

<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>
EOT

One interesting effect was that the encoded characters in the attributes (

&#xA;, &#xD;, and &#x9;

), were now coming out as unencoded characters, where previously they were just coming out as spaces. I'm still not sure what the best way would be to preserve the encoding, if I happened to care about preserving them...

Friday July 20, 2007
08:48 PM

Comparing XML docs

I've found a workaround for one of yesterday's annoyances, at least the one where a "folder compare" function (not really a file directory) in Informatica PowerCenter doesn't really compare everything in the folders. You can export folders as XML documents, so that's what I did, and then used XML::Diff to compare the documents.

Except that some of the elements in one document are not in the same order as the other document, and although I don't care, XML::Diff does. There are some commercial tools that will compare unordered XML elements, and I ran across one that I guess was free but is no longer.

So it was XML::Filter::Sort to the rescue (thank you grantm - and all the other XML folks), and I just sorted all the elements where I didn't care about the order, and then diffed the results. Several of the elements where just sorted by the name attribute, so I made a bunch of sorters in one go:(Updated code: Needed "./" prefix on NAME)

my @sorters = (
  map {
    XML::Filter::Sort->new(
      Record => $_,
      Keys => [ ['./@NAME'] ],
    )
  )  @list_of_elements
);

I also wrote my first actual XML::SAX parser for the task of deleting some attributes where I didn't care about differences in values.

And some of the attributes had encoded control characters in them e.g. , and those just came out as spaces, and for the purposes of this, I didn't care, but in other situations, I might care, so I'm wondering if there's a way to preserve those. Though I hear from a reliable secondhand source that there is no reliable way to preserve them :-(

Wednesday July 18, 2007
01:53 PM

PowerCenter annoyances (part 1)

Who designs these UI's anyway? I want to edit 8 fields to be all the same. Can I edit one and then cut and paste? Noooo....I have to click on a button, select something from a drop down list (that has hundreds of items in it), and then click "Ok".

I'm starting to think it might be easier to "Export Objects" as XML, edit the file, and then "Import Objects".

And then, after editing and saving, I do a "Folder Compare" against a previously identical folder, and they don't show any differences. What are we supposed to be comparing? If it can't find differences, how can I rely on it? (I know, export both folders as XML and then do a diff...I'm sure everyone will love that :-)(Update: I do now see in the docs that a Folder Compare doesn't compare objects that deeply...hmmm, should I point this out to everyone who's relying on some things to be identical, or allow them to live in blissful ignorance?)

(update: and I won't even go into the utter pointlessness and uselessness of the whole tool...other than having a pretty GUI to make the PHB's go ooh and ahhh)

01:20 AM

Adventures in functional idiocy

I'm no functional guru (but that doesn't stop me from trying), and I was curious about the hamming numbers (numbers with only 2, 3, and/or 5 as prime factors) and the code from MJD's book, and after a brief digression into Wikipedia (mental note to self...must work syntonic comma into casual conversation), I started thinking about MJD's code ( Fair use license verbage: from Higher-Order Perl by Mark Dominus, published by Morgan Kaufmann Publishers, Copyright 2005 by Elsevier Inc):

sub scale {
  my ($s, $c) = @_;
  transform { $_[0]*$c } $s;
}
my $hamming;
$hamming = node(1,
                promise {
                  merge(scale($hamming, 2),
                  merge(scale($hamming, 3),
                        scale($hamming, 5),
                       ))
                }
               );

I thought hmmm, I wonder (just for fun) if I can get rid of that cyclical reference. So I pull everyone's favorite combinator out of Moose::Autobox::Code (and modify it to make it non-OO) and try this:

sub u {
    my $f = shift;
    sub { $f->($f, @_) };
}

sub Y {
    my $f = shift;
    u(sub { my $h = shift; sub { $f->(u($h)->())->(@_) } })->();
}

my $hamming = Y( sub {
  my $h = shift;
  sub {
    node(1,
      promise {
      merge(scale($h, 2),
      merge(scale($h, 3),
      scale($h, 5),
    ))})
  }
});

And get:

Not an ARRAY reference at C:/Perl/site/lib/HOP/Stream.pm line 127.

Oh crap, I forgot, a HOP::Stream isn't a function, stupid, it's an array reference (C'mon, can't I just make shit up and expect the computer to know what I mean?). Let's try something else:

my $mk_hamm = Y( sub {
  my $h = shift;
  sub {
    my $s = node(1, $h);
    node(1,
      promise {
      merge(scale($s, 2),
      merge(scale($s, 3),
      scale($s, 5),
    ))})
  }
});
my $hamming = $mk_hamm->();

And get this:

1
2
2
3
3
4
4
5
5
6
6
8
8
9
9
10
10
12
12

Hey, that's close! Hmm, what happens if I try this (let's just make more shit up):

my $mk_hamm = Y( sub {
  my $h = shift;
  sub {
    my $s = node(0, $h);
    node(1,
      promise {
      merge(scale($s, 2),
      merge(scale($s, 3),
      scale($s, 5),
    ))})
  }
});

And we get:

1
0
2
0
3
0
4
0
5
0
6
0
8
0
9
0
10
0
12
0
15

Hey, now all I have to do is change this line:

#my $hamming = $mk_hamm->();
my $hamming = filter { $_[0] > 0 } $mk_hamm->();

And we get the right output. But that's still not quite satisfying, due to the extra cruft that we have to filter out. The "node" being assigned in the original is the same reference as in all of the merge clauses, so let's try just skipping the original node and just merge all the other streams that do have the same node as a basis:

my $mk_hamm = Y( sub {
  my $h = shift;
  sub {
    my $s = node(1, $h);
    merge(scale($s, 2),
    merge(scale($s, 3),
    scale($s, 5),
    ))
  }
});
my $hamming = $mk_hamm->();

And all we're missing is the initial "1" in the stream:

2
3
4
5
6
8
9
10
12

So, let's just "unshift" the "1" back on after the fact by changing this line:

#my $hamming = $mk_hamm->();
my $hamming = node(1, $mk_hamm->());

And it works! I'm still not altogether sure exactly what was going on with the previous versions, but I'm too tired now to figure it out. G'nite.