|
|
#21 |
|
League Commissioner
Join Date: Jan 2002
Posts: 5,536
|
spiff,
looks like you'd change the sec,min,hour in the timelocal call to 0,0,12 from % man Time::Local ... $time = timelocal($sec,$min,$hours,$mday,$mon,$year); ... so, test timelocal(0,0,12,$day,$month-1,$year) paul, thanks for the scriptage. i knew we were getting some good stuff when i began to understand nothing. as for that math bug wonkery, i wondered about that right off. in other unforgiving languages, these are type errors that wouldn't even compile. but perl allows you to say, "i know what i'm doing". so, 'tis always best to perform valid operations on valid values. <preachOff> |
|
|
|
|
|
#22 |
|
Major Leaguer
Join Date: Jan 2002
Location: Adelaide, South Australia
Posts: 470
|
Phew!
[[[Arrgh: neglected to see page 2. mT has already prescribed the correct medicine. Oh well, I'll leave my garbage below, and accept another slap on the wrist!]]]
OK, sleep does good things to tired brains; but it also introduces a whole heap of doubt about what's been performed the night before. I woke up thinking I might have got you to try setting a whole lot of things to 1900, but a quick check reveals that the change in my previous message should have been in from the start: it makes a lot more sense. As for changing the time to midday. The timelocal function takes six arguments. Typing "perldoc time::local" in a terminal window outputs what's expected: $time = timelocal($sec,$min,$hours,$mday,$mon,$year); (with month in 0-11, and some helpful conventions for $year). So where I've got timelocal(1,1,1,$day, $month-1,$year) you could simply substitute timelocal(0,0,12,$day,$month-1,$year) One slight restriction occurs here: this will change the *modified* date in the finder, but if the current "creation date" as displayed by the finder is earlier than 12pm on the day (eg, if they're all at 1.01am on the same day because of an earlier run of the script) then the "creation date" won't be changed. Recall that we're really only changing the unix mtime and atime here: there's no way to adjust whatever the finder is using for it's "creation time" via unix that I know of. So I hope we haven't irretrievably polluted your files with a "too-early" creation time reading along the way. Should be fine though: list view normally uses a "Date Modified" column. It might take the finder an age to display all the new Date Modified readings correctly! If they look skew (that is, they stay at 1.01am instead of 12pm) try clicking on one to check! Hmmm.... The dates as listed by "ls" in the terminal will be fine. Cheers, Paul Last edited by pmccann; 04-12-2002 at 08:33 PM. |
|
|
|
|
|
#23 |
|
Major Leaguer
Join Date: Jan 2002
Location: Adelaide, South Australia
Posts: 470
|
The joy of unpacking
Hey mT,
you were supposed to understand that thing! If there's anything in that script that doesn't make sense after reading the present message just vomit the offending lines back at me and I'll try and wade through their noxious viscosity. Forgive me if any of the following produces a "that's bloody obvious you condescending bastard" reaction!! Re the overall structure. File::Find just provides the find() function, which runs the subroutine given in its first argument for everything that it encounters while traversing its second argument. So find(\&files,$dir) invokes the files() subroutine for every item as it runs through $dir (including its subdirectories), setting the $_ variable to the name of the item, and the longer $File::Find::name variable to the full path to the item. The backslash on the \&files is because File::Find requires that the first argument be a *reference* to a subroutine, and prefixing any variable in perl with a backslash is how you obtains a reference to it. The "-d" (same as "-d $_") just tests whether $_ names a directory. I guess the line if ( my ($year,$month,$day)=($_=~/^(\d\d)\.(\d\d)\.(\d\d)/)){ is a little ugly. Unpacking that gives you something like: ------------------------------------------------------------------------ my ($year,$month,$day); # just declare the variables # Now see if the string in $_ matches 23.03.02 or similar **.**.** format # the "m" that I've inserted is optional, but shows that we're just trying # to *match* the string against the regular expression. The parentheses # around each "\d\d" pair instruct perl to capture each such expression in # variables $1 (first parenthesis), $2, and $3. [[The original version just # shortcuts this process: if you assign variables to catch the assignments # to $1,$2 etc, then perl will "do what you want".]] if ($_=~m/^(\d\d)\.(\d\d)\.(\d\d)/){ # that is, if the filename matches $1 = $year; $2 = $month; $3 = $day; # At this stage we don't know whether the ($year, $month-1 , $date) triple # is a valid date (subtracting 1 from $month because timelocal uses months # in the range 0..11). So what we do here is wrap the call to timelocal in # an "eval". This means "execute the following bit of code as a mini-script # and return undefined if that mini-script kicks up a fuss; otherwise return # the value to which the expression evaluates". So if the date is valid it # simply returns the number of seconds since the unix epoch, which is just # the sort of thing that we want to use when setting the atime and mtime # of the file. my $seconds=eval "timelocal(1,1,1,$day,$month-1,$year)"; # So now $seconds either contains a nice number of seconds corresponding to # 1.01:01 am on the day in question, or it is undefined. Now the script just # pumps out an error for the latter, or attempts the change of date using # perl's inbuilt utime() operator. To see documentation for this thing you # can just enter "perldoc -f utime", where the -f means "built-in function". # Very, very handy! ------------------------------------------------------------------------ Anyway, that gets most of the way through the thing: the only other aspect that might look strange is the print statement: print -d $_?"Directory name":"Filename"," doesn't have required format: $_ \n"; This just stirs in a test and chooses an appropriate string to print based on the result of that test. "Directory name" if it's a directory and "filename" otherwise. The ternary ? : combination is just a sort of shortcut for an if/else combo, and is probably familiar to almost everyone. Recall that the print statement in perl just sucks in a list of things to print, but is happy to evaluate simple expressions along the way. This should also remove any mystery from the second line of the files() subroutine. For example: #!/usr/bin/perl -w use strict; print chr(109),substr("Ten",0,1),uc(" rulez"); (perldoc -f .... if that's not obvious!) So what's *not* explained here? Hopefully not much: it might be worth mentioning that "return", as strategically placed throughout files(), simply bails from the current subroutine. That is, it tells find() that it has given up on this particular filename and we should move on to the next candidate. Cheers, Paul Last edited by pmccann; 04-13-2002 at 11:50 PM. |
|
|
|
|
|
#24 |
|
Triple-A Player
Join Date: Feb 2002
Location: Dallas
Posts: 51
|
Paul-
Thanks for the additional comments... it opened my eyes on a couple of issues. It's always great to see code commented by a pro. I do have a question... what is the advantage of using "strict" and preceeding the vars w/ "my" thanks- jas. |
|
|
|
|
|
#25 |
|
Major Leaguer
Join Date: Jan 2002
Location: Adelaide, South Australia
Posts: 470
|
jas,
there are a huge number of reasons to "use strict;", particularly for larger scripts. And besides, I can't really think of any reason not to use it! Ok, so what are the positives? use strict will declare a fatal error if you use a variable without first declaring it, either via an explicit declaration of global variables: use vars qw($myvar1 $myvar2); or by declaring "lexical" variables, whose scope is limited to the enclosing block of the declaration: my ($myvar1,$myvar2); You can combine such declarations with initializing the variables, as per the scripts above: my ($one,$two,$three)=(14,15,179); Why is this helpful: well suppose you have something like: Code:
#!/usr/bin/perl
open IN,'mymailfile';
while(<IN>){
my $tmp_line=$_;
# lots and lots of lines omitted!
#
if ($temp_line=~/^From /){# start of a new message
# do some fancy stuff with it
}
use strict has a heap of other uses of course, including the prevention of unwanted "symbolic references". If you're interested let me know and I'll rustle something up re this (essentially, a soft reference is when one variable contains a string and you use that string to get to another variable: hmm, that's enlightening, isn't it!! Err, something like: $c = 'a_var_name'; $a_var_name = 'some_thing'; $d=${$c}; print $d,"\n"; This sort of usage is almost always due to a coding error, so use strict forbids symbolic references, and shouts loudly when you use one. Another source of difficult to track bugs that is best avoided. (You can say "shut up, I know what I'm doing here"! if you really want to use symbolic refs in a particular piece of code, but they're often indicative of bad code, and point to the need to know more about using hashes or something similar.) OK, this is getting too long: why the "my" declarations everywhere? For a script that's self-contained (ie "in one file") a variable declared with "my" outside of any enclosing block bar the file itself is almost equivalent to a global variable of the same name (and declared via the "use vars..." approach). But if you start to develop longer scripts, "my" declarations, being "local" to the file in question, can help you to avoid namespace conflicts. That is, inadvertantly using the same global variable in different places to mean different things, and being puzzled when the results of your script are incorrect. Essentially, they stop you polluting the global namespace, and force you to think about where the variable in question should "make sense". Does this explanation help at all? I can be a bit more precise if necessary. In a pragmatic sense, you rarely *really* need global variables, and should think carefully before using such, so it's a lot easier just to get used to declaring variables with "my" by default. Cheers, Paul Last edited by pmccann; 04-14-2002 at 12:30 AM. |
|
|
|
|
|
#26 | |||||||||||||||||||
|
League Commissioner
Join Date: Jan 2002
Posts: 5,536
|
Re: The joy of unpacking
yeah, i was supposed to call my mother last week, too. i should have stipulated "...understand nothing, until i started to unravel it". you are right; this is an interesting thread and a great way to get real world problems and figure real world solutions. regardless of how trivial they may appear on the surface, the code is a good template, applicable to many problems. i appreciate your "shoring up" what little i managed to glean from docs and snippets of your code. it's a great example of the power of perl and the implicit OZ (pay no attention to that man behind the curtain!) goings on on the programmers behalf behind the scenes, under the covers, in the shadows. you can empty your garbage on my porch anytime, you condescending bastard. re: &files and the implicit & subroutine label so the notation &files is a call to a subroutine and the backslash notation passes the reference? the learning perl book calls the function by canonical name (without the &, with a footnote about seldom using &). what would the notation \files give us in the function call? or is the &files an example of the seldom usage. and passing the reference is why we omit the () part of a sub() call? thanx again for your sterling help with us duffers. |
|||||||||||||||||||
|
|
|
|
|
#27 |
|
Major Leaguer
Join Date: Jan 2002
Location: Adelaide, South Australia
Posts: 470
|
Aaah, very astute, grasshopper: you picked up my little mumble then?
Hmm, let's see if I can make this a bit clearer: declaring a function/subroutine the usual way goes something like this: sub myfunc{ my @args = @_; print "Called a function 'myfunc'\n with arguments @args\n"; } Assuming that you've declared this function *before* entering any of the following invocations, you could call the function myfunc using any of these: myfunc('raucous','nonsense'); myfunc 'raucous','nonsense'; &myfunc('raucous','nonsense'); That is, the ampersand is optional when calling the function. And so are the brackets around its arguments, **but only when it's predefined**. (The other two forms work fine even if the function hasn't yet been defined.) But when you want to take a reference to the function &myfunc the ampersand is compulsory. (You can think of the & as labelling a subroutine in the same way that a $ labels a scalar variable, an @ labels an array, and a % labels a hash. So entering \&myfunc means that you're taking a reference to the "function variable" &myfunc. You can use that thing in the same way that you use other references: via the dereferencing arrow. So the following produces the same result as any of the function calls above. my $funcref = \&myfunc; $funcref ->('raucous','nonsense'); Cheers, Paul (there is another way to get a reference to a subroutine: you use an anonymous subroutine declaration (that is, omit the name) and "catch" the return value, using something like my $subref = sub { my @args = @_; print "Called a function 'myfunc'\n with arguments @args\n";}; (Note that the final semi-colon there: its absence was driving me mad for about 15 minutes while composing this! You can use $subref exactly as per $funcref above: $subref->("raucous","nonsense"); Hmm, this message is a bit of a dog's breakfast. If I recall correctly the Learning Perl book doesn't really touch references. Given that they're the only decent way to construct more exotic data structures this is a bit of a shame (but wholly understandable!). I'll give a baby intro to references if anyone's interested. ) |
|
|
|
|
|
#28 |
|
Triple-A Player
Join Date: Jan 2002
Posts: 129
|
yes sensei, yes sensei...
![]() i'm not worthy, i'm not worthy! please do continue with your unpacking and explanations... most appreciated (as do my files too) digression: would SetFile have been able to be used in the script in lieu of utime?
__________________
Spaceman Spiff MacPro 3.33 13Gb 8TB 10.8.5 |
|
|
|
|
|
#29 |
|
Moderator
Join Date: Jan 2002
Location: Singapore
Posts: 4,237
|
pmccann,
I love your posts. They are like a black hole dropping me to a parallel universe were the laws of this world don't apply. At first it's all dark, and like a baby, I don't know anything else but cry, and at last, when my selfpity is gone, then, I move around, and I start to see a dim light coming from the edge of my brain. The more I follow the light, the more I see. Problem is, by the time it took me to dimly understand a page, you have already written two new ones, and supplied them to the masses. We mortals, ought to know our limits when talking to the gods... We silently pray for the food to keep coming our way. And thank you for your posts. Cheers... PS: May one day teach you about the speed of 'shadow', or at least compare notes on the subject? |
|
|
|
|
|
#30 |
|
Major Leaguer
Join Date: Jan 2002
Location: Adelaide, South Australia
Posts: 470
|
Thanks very much for the kind words folks: I'll set to work on producing something worthwhile, but am distinctly "bushed" at the moment. The server upgrade alluded to above had a few surprises waiting for me at work today, and they seemed to consume every ounce of energy. When it's the company mail that's playing up everything else seems to take second place!
For what it's worth we were trying to get the combination freebsd + imap-uw + stunnel to work with each other, with stunnel running in daemon mode. It kept working for a while and then failing without evident cause. Logging gave very little, we'd rebuilt imap-uw, openssl, stunnel, etc etc. In the end we seem to have a workaround in routing everything via inetd. Which is functionally fine, but somewhat unsatisfying. xdm connections still require a fix: sigh... Anyway, that's all terribly uninteresting. Suffice to say that I'm planning a short but hopefully sweet piece on the hows and whys of references in perl. Very useful little beasties they are too. I very much look forward to learning about the "speed of shadow". Sounds like a cousin of ye olde finger pointing at the moon, which is a not-entirely-silly first description of references! spiff: yep, looks like a combination of "find" and "SetFile" and various other utilities to turn a filename into a date could have done the same thing. (As you speculated, a substitution of a call to SetFile would work fine.) For those not familiar with SetFile, it's in /Developer/Tools. You can get info about how to use it by entering % /Developer/Tools/SetFile | less Cheers, Paul |
|
|
|
|
|
#31 |
|
Major Leaguer
Join Date: Jan 2002
Location: Adelaide, South Australia
Posts: 470
|
Show me your references...
OK, here's some stuff to kick off discussion: it's a bit disjointed, and not very well written --for which I apologise, I was hoping for something better-- but **it's something**. Feel free to home in on the aspects that aren't clear.
[Warning: I lied in the previous post when I said this would be a short piece! Anyway... You can think of a reference as a nickname for a perl variable. It's a simple scalar variable that can "point" to any perl variable at all (such as a complete array, or a hash, or a filehandle, or...), and allows you to access every aspect of that "pointed to" variable. Suppose we have the array: #!/usr/bin/perl -w use strict; my @array = qw(one two three); # easier than typing ('one','two','three') You make a reference to any variable by prefacing it with a backslash: my $ref_to_array = \@array; So how do we use $ref_to_array to recover the contents of @array? And why would we want to do such a perverse thing anyway? First things first: if you wanted to find the value in the first slot of @array (that is, element 0 of @array, remembering that the first element is $array[0]) you can access it via: my $first_element = $ref_to_array -> [0]; The arrow "dereferences" the reference. To get the whole array back again, we can just preface the reference with the correct type: here it's an array reference, so use an "@" symbol. my @array_back_again = @$ref_to_array; Or, somewhat more clearly (if also more verbosely) my @array_back_again_again = @{$ref_to_array}; That is, the expression {$ref_to_array} is acting exactly like the name of an array, and you can use this expression anywhere that you would normally write the name of an array. Actually the individual access can be written in like fashion: my $first_element_again = ${$ref_to_array}[0]; But this is considered too ugly for public consumption, so the arrow syntax is used almost invariably. Note, however, that the uglier version is entirely consistent with what's written above: if you want to access element 0 of the array @myarray you use $myarray[0], so if you have an array reference you can just jam it in where the array name occurs, giving the expression ${$myarray_ref}[0]. In fact the preceding description of how to use references is very general: for example, the same things for a hash would look like: my %hash = ('one' => 'uno', 'two' => 'due'); my $hash_ref = \%hash; my $hash_value_for_key_one = $hash_ref -> {'one'}; # or ${$hash_ref} -> {'one'} my %hash_back_again = %$hash_ref; Note how the bracket type after the dereferencing arrow is consistent with the variable type: square brackets are used for arrays and curly brackets for hashes. OK, with a lot of the mechanics out of the way above, it's probably time to start delving into the question of why anyone should be interested in these things. And the answer's not too difficult to find when you realise that (i) perl arrays can only contain scalar values, and (ii) hash values in perl can only be scalar values : what good are such limited data structures when we'd like to be able to build lists of lists and lists of hashes of hashes of lists and so forth. We would? Certainly. Be patient! Complexity will come! Let's chuck around some arrays in the next post, and I'll finish with a rather humongous parsing of a library export file from iTunes. Cheers, Paul |
|
|
|
|
|
#32 |
|
Major Leaguer
Join Date: Jan 2002
Location: Adelaide, South Australia
Posts: 470
|
lists of lists
#!/usr/bin/perl -w
use strict; # Note that this whole post is just one big script. That is, you # should be able to just cut and paste the thing, then run it. # I hate always having to print the newline at the end of any # printed line: so just use this thing instead: sub nprint { print @_,"\n"; } nprint "one"; nprint "two"; # it works! my @list1 = (1,2,9); my @list2 = (3,4,5); my @list=(@list1,@list2); # the list you get is "flat", and *not* a list of lists nprint join "**",@list; # 1**2**9**3**4**5 nprint "\n",$list[3]; # 4 (Yes, Veronica, the list *is* flat) # Let's try the same sort of thing using array references: my $list_ref1 = \@list1; my $list_ref2 = \@list2; my @list_of_refs=($list_ref1,$list_ref2); nprint "\n",join "**",@list_of_refs; # Ewww, something like ARRAY(0x13550)**ARRAY(0x13580) # nprint "\n",$list_of_refs[3]; # If you uncomment the above print statement you'll receive an # error: there is no such element. @list_of_refs has only 2 entries # OK, so how do we access the elements of @list_of_refs ? nprint join "**",$list_of_refs[1]->[1]; # prints 4 nprint join "**",$list_of_refs[1][1]; # same thing, prints 4, as the arrow between any pair of brackets is # optional: note that there's no ambiguity involved, as there's no such thing # as a "real" two dimensional array. It's all done with smoke and references. # That is, there's no way that you could "really" have two brackets nestling # up against one another. # But this is all incredibly tedious: who wants to go making up lists just to # allow a reference to be taken. And who wants to invent all those horrible # names for the intermediate arrays? We've got the data, let's just make the # structure directly. And that's where anonymous array references come in. # You can create anonymous array references that play the same role as the # refernces defined above (but without declaring redundant arrays) as follows my $anon_ref1 = [1,2,9]; my $anon_ref2 = [3,4,5]; my @list_of_anon_refs = ($anon_ref1,$anon_ref2); # Gack! We're doing it again: too many variable names! Cutting to the # chase then , we can declare a "two dimensional array" using my @two_d_array = ([1,2,9],[3,4,5]); # and access the third element in the second component of @two_d_array via nprint $two_d_array[1] -> [2]; # But remember that arrow is optional here, so nprint $two_d_array[1][2]; # Same thing: looks very 2-d matrix-like now! nprint "-"x72; # whack a separator in our output # How about printing the thing out "neatly" as a matrix? # The mundane foreach my $element (@two_d_array){ nprint join"\t",@{$element} } nprint "-"x72; # Slightly more fun, but not as readable?? nprint join("\t", @{$_}) foreach (@two_d_array); nprint "-"x72; # And simply stupid ( nprint join "\n", map {join "\t",@{$_}} @two_d_array; # OK, that's enough of that stinky crud. The next post is a pudding in which # some form of proof may have been inserted. Hope your hungry. |
|
|
|
|
|
#33 |
|
Major Leaguer
Join Date: Jan 2002
Location: Adelaide, South Australia
Posts: 470
|
Tune-ups
I struggled for a while to find a suitable candidate for a bit of data munging via references. Hashes are the best candidates (unless you go back to matrices, but having taught maths at uni for far too many years I tend to avoid matrix operations like the plague!). So here's a somewhat real-world "tool" that operates on a list of songs exported from iTunes.
In iTunes go to "Advanced/Export Song List..." and get yourself a text file listing of all the music that you have imported. If you're library isn't too stupendously large you might like to try exporting the whole thing. Yeah, give it a go! iTunes makes a text file with the following format: all items are tab separated, and I've only shown the line of headers and a single song. The line breaks are "classic Mac", which we'll work around in the script below. Name Artist Album Genre Size Time Track Number Track Count Year Date Date Added Bit Rate Sample Rate Volume Adjustment Kind Equalizer Comments Location For Which We Are Truly Thankful Lambchop How I Quit Smoking 3598693 179 1 14 30/10/01 4:08 PM 30/10/01 1:12 PM 160 44100 0 MPEG audio file osx:Users:pmccann:Documents:iTunes:iTunes Music:Unknown Artist:How I Quit Smoking:For Which We Are Truly Than.mp3 I've shown two lines of the output: the header and a single track from a single album. Imagine that this is "significantly" larger than shown, and contains, for example, the songs from several albums by the same artist. We'd like to produce semi-useful summary information about the music library. I'll try and keep it tractable; other items should be easy to add in and fool around with if you've understood the script below. It's reasonably heavily commented, but even with that I'm not going to pretend that the script is *easily* understood. But I'm done with trying to invent stuff and will revert to question answering mode for a while! Cheers, Paul (back to polluting boards again... sorry robg!) #!/usr/bin/perl -w use strict; my $export_file = '/Users/pmccann/Desktop/Library.export'; # Change this to where you save your iTunes export file $/="\015"; # itunes exports with "classic" mac line endings, so we need to # let perl know this. $/ is the "chunk separator", which is a unix # end of line character by default (ie "\012"). my %song_info; #hash to hold all the info of interest open IN,$export_file or die "infile error: $!"; my @headings = split "\t",<IN>; # headings are in the first line of the file # We don't actually do anything with them here, but you might want to! while (<IN>){ my ($artist,$album,$song,$time,$track)=(split "\t",$_)[1,2,0,5,6]; # Just picking out the 5 chunks of information that interest us here! $song_info{$artist}{$album}{$song}{'time'}=$time; $song_info{$artist}{$album}{$song}{'track'}=$track; } # Ouch: we're building something of a monster here! %song_info is a hash whose # keys are artist names, and whose values are hash references. Each such hash # reference "has" keys the album names of the artist concerned, and values # another hash reference. The keys of each of *these* hashes are the song titles # and the values are **another** hash reference. These final hashes have two # keys, the words 'time' and 'track', and the corresponding values are the # number of seconds the track runs for and the number of the track on the album. # That's immensely easier to program than unpack correctly! So %song_info is a # hash of hashes of hashes of hashes. # Now try to get something useful out of the other end!! # First let's get a simple list of the artists my @artists = sort keys %song_info; # the keys of the hash are the artists print "Artists: ", join(", ", @artists),"\n\n"; # How about a list of the albums by each artist? foreach my $artist_item (@artists){ print "-"x25,"\n"; # Just as a separator my @albums = sort keys %{$song_info{$artist_item}}; # $song_info{$artist_item} is a hash reference, so we're just asking for # the keys of the hash that this reference "refers to/points to". print "Artist: $artist_item\n","Albums: ",join(", ",@albums),"\n","-"x25,"\n\n"; } # And the songs for each album by each artist? foreach my $artist_item (@artists){ my @albums = sort keys %{$song_info{$artist_item}}; # Just as above print "Artist: $artist_item\n"; print "-"x25,"\n"; # Just as a separator foreach my $album_item (@albums){ print "Album: $album_item\nContains the tracks\n","-"x25,"\n"; my @songs = sort { $song_info{$artist_item}{$album_item}{$a}{'track'} <=> $song_info{$artist_item}{$album_item}{$b}{'track'} } keys %{$song_info{$artist_item}{$album_item}}; #Yech! See below print join("\n",@songs),"\n\n"; } } # The yech! comment is appropriate: is there a nicer way to write this stuff # that avoids all the repetitive use of "$song_info{$artist_item}{$album_item}? # Yep! I might do that later. For now you might just like to know what's going # on in there. We're obtaining the keys of the hash that we get via the hash # reference $song_info{$artist_item}{$album_item}, which will be the titles of # the songs on the album in question (that is, $album_item), and we're # numerically sorting those keys based on the value of the 'track' item for # that song. In other words, we're printing the songs in the same order that # they appear on the album. OK, here's the slightly easier way to write the # above output. I've commented it out here so that you don't get the output # twice. The key is the "my $ref...." line, which just gives you a shortcut # to that horrid name. Notice that I had to insert two dereferencing arrows # in the third last line: the arrow can only be left out when there are # "back to back brackets". Not the case here. # foreach my $art_item (@artists){ # my @albs = sort keys %{$song_info{$art_item}}; # Just as above # print "Artist: $art_item\n"; # print "-"x25,"\n"; # Just as a separator # foreach my $alb_item (@albs){ # my $ref = $song_info{$art_item}{$alb_item}; # print "Album: $alb_item\nContains the tracks\n","-"x25,"\n"; # my @songs_2 = # sort { $ref->{$a}{'track'} <=> $ref->{$b}{'track'} } keys %{$ref}; # print join("\n",@songs_2),"\n\n"; # } # } # Finally, how about using the $time information for each song to produce a # summary of the total playing time of each "album"? foreach my $artist_item (@artists){ my @albums = sort keys %{$song_info{$artist_item}}; print "Artist: $artist_item\n"; print "-"x25,"\n"; # Just a separator foreach my $album_item (@albums){ my $total_time; print "Album: $album_item\n"; my @song_list = keys %{$song_info{$artist_item}{$album_item}}; foreach my $song_item (@song_list){ $total_time += $song_info{$artist_item}{$album_item}{$song_item}{'time'}; } printf "Total playing time: %d minutes, %d seconds\n\n",$total_time/60,$total_time%60; } } Last edited by pmccann; 04-16-2002 at 10:44 AM. |
|
|
|
|
|
#34 |
|
Moderator
Join Date: Jan 2002
Location: Singapore
Posts: 4,237
|
pmccann,
A little late but was trying to catch up with all this, Great stuff...! Tried the last script and worked like a charm ! A little bit from every section: ------------------------ Artist: Africando Albums: , 12 Hits, Baloba, Gombo Salsa ------------------------- ------------------------- Artist: Afro Cuban All Stars Albums: A Toda Cuba Le Gusta, Distinto, diferente ==============================x Artist: Ian Sheffield ------------------------- Album: Ian Sheffield's Greatest Hits Contains the tracks ------------------------- Muppets - Hugga Wugga Artist: Ibrahim Ferrer ------------------------- Album: Buena Vista Social Club Contains the tracks ------------------------- Cienfuegos Tiene Su Guaguanc? ==============================x Album: SUS PRIMEROS EXITOS Contains the tracks ------------------------- La Atardecida La Nostalgiosa Qu? Bonita Va Coplas Del Valle No Te Puedo Olvidar ==============================x Artist: Royal College of Music Chamber ------------------------- Album: Carols For Christmas - Vol 2 Contains the tracks ------------------------- Good King Wenceslas ==============================x Artist: Vienna Boys Choir ------------------------- Album: Season's Greetings Total playing time: 3 minutes, 42 seconds Artist: Walt Disney Records ------------------------- Album: Classic Disney Volume II Total playing time: 2 minutes, 42 seconds ==============================x Only got a couple of: Argument "" isn't numeric in numeric comparison (<=>) at ./pmccann line 55, <IN> chunk 218. Sorry, I named the script 'pmccann' What can I say, bow respectfully to the master. Thanks! Cheers... |
|
|
|
|
|
#35 |
|
Moderator
Join Date: Jan 2002
Location: Singapore
Posts: 4,237
|
pmccann,
I almost forgot about the "speed of shadow". Well, not too serious, but from philosopher to pseudo=scientist is the way I followed. I always believed that Einstein was missing some good points. And I came to believe it, more and more in my theories, then I met, some years ago, a famous subatomic particles physicist, during a retreat into a spiritual training, in the mountains sorrounding Florence, in Italy. After a few days, during a short break, we exchanged ideas. And he told me, he was trying to prove that Einstein was wrong, and since the last two years he was writing his theories. I told him I agreed with him, and he asked me how did I get to that conclusion. I answered because of the speed of shadow, as opposed to the speed of light. Shadow is faster, I answered. And asked how himself got the same conclusion. He answered because light doesn't travel in emptiness. Einstein figured the speed of light as travelling through empty space. And that was a flaw according to him. More and more, we see that emptiness is full, and what is considered full, is really empty. In the differences between the behavior of matter and that of antimatter. In the quest to catch a neutrino (the elusive mass). Looking at your scripts and realizing the terrible importance of the empty spaces. Missing just one and the script doesn't work. ![]() Well, as I said nothing too serious, and I could really go on forever with this, but is not the time and place to do it. Sorry to maybe waste your time. ![]() Please, keep those scripts coming, they are pure gold. Cheers... Last edited by sao; 04-17-2002 at 03:02 PM. |
|
|
|
|
|
#36 |
|
Major Leaguer
Join Date: Jan 2002
Location: Adelaide, South Australia
Posts: 470
|
*Excellent* intro perl references tutorial
Never overlook the obvious!
Here I was searching and searching for an old article that Mark-Jason Dominus wrote for "The Perl Journal", and what do I find as part of the *base* perl distribution but that very article. Freaky! If anyone has looked at the posts above and felt like throwing up I really recommend opening a terminal window and entering: % perldoc perlreftut Do this before trying to make sense of my stuff. It does what I would really like to have achieved above, and gives a quick, clean introduction to --and motivation for-- using references. Definitely complements my bilge pretty well. (If anyone thinks I'm going to go near the "word" 'prequel' in an unbracketed sentence they are seriously delusional!) [[Hi Sao, nice to hear that it "pretty much" worked: I wonder what's in the "track" info for the troubled tracks (ie those producing the warning)? Could be an empty string perhaps, if that information hasn't been entered into itunes. Hmm. Probably not worth chasing (the problem is that "<=>", the "spaceship operator" is used for comparing values numerically, returning -1, 0 or 1 depending on whether the entity on the left hand side is less than, equal to, or greater than the entity on the right of the spaceship. It'll kick up the sort of fuss that you saw if there's a non-numerical argument. I'd guess that we've got a non-existent field.) You could probably silence this thing by setting the "track" to 0 (or 1 if you like) if it doesn't exist. That is, insert the following line after the one that sets $song_info{$artist}{$album}{$song}{'track'}. $song_info{$artist}{$album}{$song}{'track'}|| = 0; (That is, if the it's currently an empty string (or undef) or 0, and thus is "false" in perl's true/false distinction, just set it to 0 and be done with it. Oh yeah: you wouldn't be an engineer by any strange chance? All the people I know of, or have read about, who wish to disprove relativity seem to be engineers! And mostly *electrical* engineers, though I've really got no clue about *why*. You're going to have a lot of difficulty getting a FTL shadow into my universe: I've been massively indoctrinated via a PhD in mathematical physics: Special Relativity is my friend (and even deserves its caps)!! But yeah, here is definitely not the place for such a discussion in any case. Best wishes, Paul]] |
|
|
|
|
|
#37 |
|
Moderator
Join Date: Jan 2002
Location: Singapore
Posts: 4,237
|
pmccann,
Thanks again for coming to the rescue. I did the insertion: $song_info{$artist}{$album}{$song}{'time'}=$time; $song_info{$artist}{$album}{$song}{'track'}=$track; $song_info{$artist}{$album}{$song}{'track'}|| = 0; } but now in terminal comes out: [pm:/Users/pm/Desktop]./pmccann syntax error at ./pmccann line 20, near "|| =" Execution of ./pmccann aborted due to compilation errors. [pm:/Users/pm/Desktop] What I did wrong? By the way, I started reading the Perl References Tutorial and it's great. There must be somethings wrong, because it's seems easy to understand. Thanks for the tip. Cheers... PS: No, I'm not an engineer (altgough my father wanted me to become one) I am more of a schlump. |
|
|
|
|
|
#38 |
|
Major Leaguer
Join Date: Jan 2002
Location: Adelaide, South Australia
Posts: 470
|
Aaah, sloppy sloppy me
Hi again,
sorry, that's what I get for typing instead of proofreading/testing. It's a very simple thing: there should be no space between the or operator ( '||') and the equals sign. '||=' is a shorthand operator, and must be typed correctly. Unsurprisingly. Sorry re the sloppy! Paul |
|
|
|
|
|
#39 |
|
Moderator
Join Date: Jan 2002
Location: Singapore
Posts: 4,237
|
pmccann,
Thank you. Now it works very fine. I got just one instance of: Argument "" isn't numeric in addition (+) at ./pmccann line 101, <IN> chunk 218. Cheers... |
|
|
|
|
|
#40 |
|
Major Leaguer
Join Date: Jan 2002
Location: Adelaide, South Australia
Posts: 470
|
But that trick *never* works...
Oh boy,
I guess you can probably see where that one's coming from. If the size isn't set we should set it to be zero (?) as well. At least this will stop the script from whinging (very fairly!) about adding up empty strings. But the above fix for track numbers is ugly: let's mix it into the declaration instead. Delete the '||=' line given above, and simply change the original lines to read: $song_info{$artist}{$album}{$song}{'time'}=$time||0; $song_info{$artist}{$album}{$song}{'track'}=$track||0; Hopefully you hear nought but the sounds of silence. Cheers, Paul (pushing polyfilla into way too many holes lately) |
|
|
|
![]() |
|
|