Tuesday, July 08, 2014

Class Abuse

This situation arose recently and it got me curious...

What if you needed to access an algorithm in a very simple class method, but the functionality of the class is significant and would be a pain in the ass to set up and create an object in, not to mention resource-intensive, all to use one small function...

You could copy the algorithm, but what if it changes? Then you'll have to change it twice. That's no good. I don't like either option.

Remember when you call an object's method (or a class method) that the indirection syntax $self->method() or Whatever::Class->method() sends the thing before the -> as the first parameter.

So what if I just call that function directly? In some cases, I certainly could, for example: Whatever::Class::method($mydata)

In other cases, where the method looks in $self, I could just send the appropriate type of reference in with the value I needed, for example: Whatever::Class::method({data => $mydata})

In this case, it calls an accessor method -- in my example here, get_data() -- to get the value of one of its own properties. This is in line with some OOP philosophies, but it makes use of that method as an individual function that much more difficult.

However, I can just call the function with a first parameter which is an object that responds to the call to get_data

I wrote this naive decoder-ring algorithm to demonstrate.

#!/usr/bin/perl

use 5.14.0;
use autodie;

package Decoder_Ring;

sub new {
    my ( $class, $data ) = @_;
    return bless { data => $data }, $class;
}

sub decode {
    my ($self) = @_;
    return join( '',
        ( split( '', $self->get_data ) )
          [ 2, 7, 17, 24, 31, 37, 45, 54, 55, 65, 72, 77 ] );
}

sub get_data {
    my ($self) = @_;
    return $self->{data};
}

package main;

# Here's the normal object creation and method call to decode
my $normal_object =
  Decoder_Ring->new('?sSY y#ee Uiehor$cBc yL@rekLt AevLQYotUasjCGu!Z!a"');
print "normal result: ", $normal_object->decode, "\n";

# and here's the "fake" one:
# First, bless into unique class name to avoid polluting your namespace
my $pretender_object = bless {}, 'MyTemporaryClass';
my $input_data = 'l2a*lP lu*j!BfeCxssov@osol aupu Zwv+us P'
               . 'VSMvxekv vegA*crsY$L$2^ Sei!uVC$t!N?4.3';

# Next, push a method into the symbol table of the newly created class
*{MyTemporaryClass::get_data} = sub { return $input_data };

# Now our ad-hoc object will respond appropriately to the get_data call!
print "pretender result: ", Decoder_Ring::decode($pretender_object), "\n";


But, I wouldn't put this in production code and I wouldn't recommend anyone else do it either, or at least comment heavily if you must.  However, it is an interesting exercise and illustrates Perl's highly flexible OO implementation.

Sunday, June 08, 2014

My First Crontab




Ok, it's not the first crontab I've ever written. This is my crontab template that I just created for mysellf because even though I've been using cron since the late 80s, I still keep forgetting which side is minutes. How sad is that?!?

I copied the nice format from Wikipedia's cron page.

The first line (the only line that isn't a comment, and therefore an actual crontab entry) is my initial test to make sure cron is working for the account.

   * * * * *  echo hi >/tmp/a.hi
#  * * * * *  command to execute
#  _ _ _ _ _
#  | | | | |
#  | | | | |
#  | | | | +---day of week (0 - 6)
#  | | | +--------month (1 - 12)
#  | | +-------------day of month (1 - 31)
#  | +------------------hour (0 - 23)
#  +-------------------------min (0 - 59)

Thursday, May 08, 2014

When a Tradeoff is not a Tradeoff


Years ago, I wrote a post on clever code and advised against it, saying that instead we should strive to make our code maintainable. Since then, I gave a talk at DCBPW called Writing Maintainable Perl. During the talk, I broke a single line of code into multiple lines of code. A member of the audience pointed out that this could have performance ramifications because every time there is a semicolon, Perl does some cleanup and other maintenance tasks. I don’t know much about Perl internals. I have since looked around for more info on this, but Google was not my friend in this case, and perlguts didn’t seem to have what I was looking for in this department either.

In the real world this type of optimization is unlikely to make much of a difference. If you have code that’s dealing with I/O from users, databases, filesystems, etc, an internal language mechanism is just not going to be a factor.

Anyway… thinking back to Damian Conway’s book Perl Best Practices, I remembered a suggestion: “Don’t optimize; benchmark.” whose accompanying text proposed that instead of looking through the code for things you might improve, you should instead run Benchmark tests against your code and see how it performs.

I decided to put both versions of the code to the test and see how they perform. Here are the results:

10:54 dbradford@dbradford-PC presentation ] > ./both_listifys.pl
Benchmark: timing 5000000 iterations of good_listify, orig_listify...
good_listify: 20 wallclock secs (19.75 usr +  0.19 sys = 19.94 CPU) @
250789.99/s (n=5000000)
orig_listify: 23 wallclock secs (22.01 usr +  0.62 sys = 22.64 CPU) @
220887.08/s (n=5000000)

“good_listify” is the cleaned up version of the routine and “orig_listify” is the “bad” version. As you can see, the difference was negligible - when I ran the test five million times there was only a difference of three seconds. However, it was a surprise to me to see that the more maintainable version was faster.

Sometimes it seems like we might trade away speed to get maintainability, but if you go back and check, you might find it doesn’t have to be a tradeoff at all. Taken to a wider view: don’t theorize; demonstrate!

Here is the code that produced the output above:

#!/usr/bin/perl

use strict;
use warnings;

use Benchmark qw(:all) ;
use Data::Dumper;

sub orig_listify {
   my ($aref,$cc) = @_;
   if( ref $aref eq 'ARRAY' && $cc > 0 ) {
       my $j;
       for(my $i=0; $i<=$#$aref; $i+=$cc) {
           push @$j, [@$aref[$i..$i+$cc-1]];
       }

       # BAD!
       $#{$j->[$#{$j}]}=$#$aref%$cc;

       @$aref = @$j;
       return 1;
   }
   return;
}

sub good_listify {
   my ( $in_aref, $elements_per_array ) = @_;
   return if (
       ref $in_aref ne 'ARRAY' or
       $elements_per_array <= 0
   );
   my @result_array;
   for( my $i = 0; $i <= $#$in_aref; $i += $elements_per_array ) {
       push @result_array, [
           @$in_aref[ $i..$i + $elements_per_array - 1 ]
       ];
   }
   my $final_aref        = $result_array[ -1 ];
   my $elements_in_final = $#$in_aref % $elements_per_array;
   # Truncate final array
   $#$final_aref = $elements_in_final;
   @$in_aref = @result_array;
}

my @my_array = ( 'one', 'two', 'three', 'four', 'five', 'six', 'seven',
'eight', 'nine', 'ten' );

my $count=5_000_000;

timethese($count, {
    'orig_listify' => sub { orig_listify(\@my_array, 4) },
    'good_listify' => sub { good_listify(\@my_array, 4) },
});

Monday, February 24, 2014

Convert Unix man page to PDF




Thanks to this post by Aron at True EDGE, I learned how to convert a Unix man page to a PDF file:

man -t bash | ps2pdf - bash.pdf

Combined with my iPad, and the GoodReader app (in which I can sync a subdirectory from my Dropbox), and I can save man pages for reading later in a more comfortable setting.

Saturday, February 22, 2014

Changing the Lock Screen Wallpaper in Lubuntu



I noticed that after changing my desktop wallpaper in Lubuntu 13.10 "saucy" (right click on the desktop and choose Desktop Preferences, Appearance tab), that the image on my lock screen had not changed. I don't love that wallpaper, so I wanted to change it. After searching the web and not finding a way to do so, I tried a few things and finally ran a locate for "wallpaper" finding, among other things, these files:

dave@tyrant:~$ locate wallpaper
(output has been truncated to display only relevant files)
/usr/share/lubuntu/wallpapers
/usr/share/lubuntu/wallpapers/1310-A_Winter_Magic_by_Luciash_D-Being.jpg
/usr/share/lubuntu/wallpapers/1310-Moody_by_Robert_Wicek.jpg
/usr/share/lubuntu/wallpapers/1310-Muelle_by_Manuel_Puentes.jpg
Listing the contents of that directory gave me a little more insight:
dave@tyrant:~$ cd /usr/share/lubuntu/wallpapers
dave@tyrant:/usr/share/lubuntu/wallpapers$ ls -l
total 5260
-rw-r--r-- 1 root root  890508 Sep 19 11:57 1310-A_Winter_Magic_by_Luciash_D-Be
ing.jpg
-rw-r--r-- 1 root root  585583 Sep 19 14:41 1310-lubuntu-default-wallpaper.png
-rw-r--r-- 1 root root  370825 Sep 19 11:57 1310-Moody_by_Robert_Wicek.jpg
-rw-r--r-- 1 root root  461580 Sep 19 11:57 1310-Muelle_by_Manuel_Puentes.jpg
-rw-r--r-- 1 root root  835281 Sep 19 11:57 1310-Smolikas_by_George_Blades_Voul
garakis.jpg
-rw-r--r-- 1 root root 2175372 Sep 19 11:57 1310-Two_Jack_Lake_by_C_Ayers.jpg
lrwxrwxrwx 1 root root      29 Feb 11 12:49 lubuntu-default-wallpaper.jpg ->
lubuntu-default-wallpaper.png
lrwxrwxrwx 1 root root      12 Feb 22 10:54 lubuntu-default-wallpaper.png ->
./1310-lubuntu-default-wallpaper.png
So, I copied the picture I wanted as my wallpaper into this directory and re-established what seemed to be the operative link:
dave@tyrant:/usr/share/lubuntu/wallpapers$ sudo su - # be careful after this as
we are now root
root@tyrant:~# cd /usr/share/lubuntu/wallpapers
root@tyrant:/usr/share/lubuntu/wallpapers# cp /home/dave/unnecessarily_long_web
_name.jpg ./nifty_colors.jpg
root@tyrant:/usr/share/lubuntu/wallpapers# ln -fs ./nifty_colors.jpg ./lubuntu-
default-wallpaper.png
root@tyrant:/usr/share/lubuntu/wallpapers# exit
logout
dave@tyrant:/usr/share/lubuntu/wallpapers$
And it worked! Using this method I have a different wallpaper on my desktop than on my lock screen, which is also cool.

Monday, January 27, 2014

Confusion Between Target Host and Local Host in SSH Tunnel


I was trying to tunnel VNC connections over SSH and I encountered a problem caused by a mistake I always make, setting up the SSH tunnel on the host that's going to VNC-view the target host:

$ ssh -f user@target_host -L 5905:target_host:5901 -N

I try to connect via this tunnel and I see:

channel 2: open failed: connect failed: Connection refused

I should have created the tunnel like this:

$ ssh -f user@target_host -L 5905:127.0.0.1:5901 -N

Then, VNC-ing to port 5905 on 127.0.0.1 works.

Tuesday, December 10, 2013

xwords

An "xword" is part of a system I created to automate some of the routine things in my life. It is somewhat similar in function to a hashtag, in that it categorizes text without being part of the text itself.

To follow my examples, it helps to know that I use the iPhone app Captio (which I recommend) to send myself emails from my phone. It will send every email you create to the same address that you specify in the settings.  This means that to send yourself a simple reminder note, open the app, type just your text (for example: "wash car") and press send. It's handy if you email yourself a lot with reminders and notes. It also allows you to attach a photo.

In my case, the complete message to myself would be:

xdo wash car

"xdo" is an xword I use to tell my email account to forward the item to my Remember the Milk (also recommended and free) account as a To Do item. In Gmail, this can be accomplished by creating a filter.

Now, why an xword and not a normal word or even a word combination like "todo?" Because that might appear in normal text, say, something someone else might send you. If it does, the filter will act upon that email and you'll need to deal with it each time. An xword that you create should not be a real word. This way, the chances of it appearing in text where you did not intend it to be are slim.

Why not a hashtag or symbol of some sort? I want my solution to be "application-agnostic" meaning I don't want it to work in some places and not others. Software is inconsistent at dealing with and parsing words containing symbols, so to keep it simple, I want to use only alphanumerics.

Another thing I like to track is when I have finished a book. That message will look like this:

xred Peopleware: Productive Projects and Teams by Tom DeMarco and Timothy Lister

This triggers a separate filter which tags the email and archives it after forwarding it to my Evernote account.

xwords are also a great way to "tag" something that doesn't allow for tags. The "tags" can be used anywhere. You could rename a series of photos to end with "_xhivn" so that "dave_hiking.jpg" becomes "dave_hiking_xhivn.jpg" so that you can search for files containing "_xhivn" on your computer to find all of those files which relate to your Hawaii vacation (photos, maps, receipts, etc.)

You can also use multiple xwords, just like tags:

xdo xhivn get plane tickets

As a final thought, it's best not to tell people your actual xwords and the email account in which you use them, for obvious reasons. ;)