Re: Corrupt Data?
by friedo (Prior) on Jul 14, 2008 at 19:41 UTC
|
The reason you're getting garbage at the end of your loop is because you're pushing stuff onto @array, then shifting stuff off the front, but looping over @array sequentially. You can't alter the thing you're looping over and expect sane results.
BTW, you don't need to use a C-style for loop to iterate over an array, it's much easier to write it this way:
my @results;
foreach my $addr( @array ) {
my $element = lc $addr;
# mess with $element here
push @results, $element;
}
return @results;
You also don't need to reset @hold each time through the loop if you declare it with my, since it will be lexically scoped to the enclosing block. You'll catch a number of other problems if you turn on strict and warnings; put this at the top of your script:
use strict;
use warnings;
and fix stuff until it stops yelling at you. Finally, the print @array statement at the end of your sub will never get executed because you return on the previous line!
I think that should be enough to get you well on your way. Good luck!
| [reply] [d/l] [select] |
|
|
| [reply] |
|
|
But he is accessing it positionally (with the for loop) while pushing onto it and shifting it at the same time. That means he's going to end up running his transformation on output data, plus throwing away data that should have been transformed, etc.
| [reply] |
|
|
|
|
Thanks for your posts, guys!
Almut - I thought that the variable @_ would take whatever was passed to it, even for a subroutine without any explicit arguments? Therefore, I should be able to pass either a scalar or an array, without having to explicitly define arguments.
friedo - Thanks for your help in finding out how I was corrupting my own data.
Regards,
Scott
| [reply] |
|
|
I thought that the variable @_ would take whatever was passed to it,
even for a subroutine without any explicit arguments?
Thing is that if you declare sub convert () {...} you're telling
Perl that the routine takes no arguments... In other words, if
you're then going to call the routine with any arguments, you'll just
get the error Too many arguments for main::convert ...
Without any prototype, however, Perl already does exactly what you
want, i.e. you can pass one or several arguments...
| [reply] [d/l] [select] |
|
|
even for a subroutine without any explicit arguments
I don't know where you got the term explicit or what you want it means in this context, but what almut meant was that the "()" in "sub foo () { ... }" means the subroutine takes no arguements. Drop the "()": "sub foo { ... }".
Update: Too slow and credited the wrong monk. Fixed the latter.
| [reply] [d/l] [select] |
Re: Corrupt Data?
by almut (Canon) on Jul 14, 2008 at 19:37 UTC
|
How are you calling the convert() routine? You're
declaring a no-arguments prototype (i.e. the empty parentheses), but are then assigning my @array = @_; (presumed
to be arguments) at the beginning of the routine. This doesn't make much sense.
Similarly, printing something after the return ... :)
Update: BTW, you could also use unpack to simplify
the function, e.g. something like
my @mac_addrs = ("0015FAA3F03A", "0015FAA3F03B", "0015FAA3F03C");
sub convert {
return map { "AP" . join ".", unpack "(A4)*", lc $_ } @_;
}
print "$_\n" for convert(@mac_addrs);
Output:
AP0015.faa3.f03a
AP0015.faa3.f03b
AP0015.faa3.f03c
| [reply] [d/l] [select] |
Re: Corrupt Data?
by ikegami (Patriarch) on Jul 14, 2008 at 20:36 UTC
|
Aside from what's already been said,
my $j = 0; for ($j; $j<$length; $j++)
could be better written as
for (my $j = 0; $j<$length; $j++)
and even better as
for my $j (0..$#array)
| [reply] [d/l] [select] |
|
|
Thanks for the discussion everyone...I always do my homework first, but the web has just as many bad examples and poorly written documentation as it does good ones. In any case, the goal here is for all of us, particularly myself, to become better programmers. To that end, since I didn't find any examples as unique as Almut's use of the unpack function, do I understand its use thusly:
return - we want to return a value
map - we're going to use the map function to evaluate each array node
"AP" . join "." - I understand what this is doing, but I don't understand from any documentation what the first argument to map is, generally speaking (everything before the first comma)
unpack - I see from documentation that the capital 'A' for unpack refers to any ASCII character with its whitespace, but again I don't understand from documentation that you can add the ordinal and/or * along with it, and how that modifies the query
lc $_ - perform the lowercase operation on the current scalar/node of the array
@_ - Store the results to the unnamed array @_
If anyone has any additional comments and/or suggestions regarding my understanding, please feel free. In addition, any suggestions for comprehensive documentation is appreciated. I currently use search engines to find as many examples of a particular piece of code until it clicks with me and I understand it.
Regards,
Scott
| [reply] |
|
|
I will (too) try to explain and detail almut's answer:
my @mac_addrs = ("0015FAA3F03A", "0015FAA3F03B", "0015FAA3F03C");
sub convert {
return map { "AP" . join ".", unpack "(A4)*", lc $_ } @_;
}
print "$_\n" for convert(@mac_addrs);
Well:
- @answer = map { do_something_with("$_") } @array transforms one @array into another array @answer where each element of @answer is the result of the transform do_something_with(). Try this:
$ perl -le '@a = (2, 3, 4, 5); @b = map { 2 * $_ } @a; print for @b'
4
6
8
10
Notice that @b has each of the elements of @a, doubled. The result of the block given to map can be an array, too, so:
$ perl -le '@a = (2, 3, 4, 5); @b = map { $_ % 2 ? () : ($_, $_ / 2) }
+ @a; print for @b'
2
1
4
2
Notice that it printed the number and its half -- for the even numbers in @a.
- So, let's try the knowledge of map:
$ perl -le '@mac_addrs = ("0015FAA3F03A", "0015FAA3F03B", "0015FAA3F03
+C"); @a = map { lc $_ } @mac_addrs; print for @a'
0015faa3f03a
0015faa3f03b
0015faa3f03c
- Now, let's add unpack"(A4)*" to the equation (unpack any number of groups of four alphanumeric chars):
$ perl -le '@mac_addrs = ("0015FAA3F03A", "0015FAA3F03B", "0015FAA3F03
+C"); @a = map { unpack "(A4)*", lc $_ } @mac_addrs; print for @a'
0015
faa3
f03a
0015
faa3
f03b
0015
faa3
f03c
- Now, let's join the parts with ".":
$ perl -le '@mac_addrs = ("0015FAA3F03A", "0015FAA3F03B", "0015FAA3F03
+C"); @a = map { join ".", unpack "(A4)*", lc $_ } @mac_addrs; print f
+or @a'
0015.faa3.f03a
0015.faa3.f03b
0015.faa3.f03c
- And then, add the "AP" in the beginning:
@mac_addrs = ("0015FAA3F03A", "0015FAA3F03B", "0015FAA3F03C"); @a = ma
+p { "AP" . join ".", unpack "(A4)*", lc $_ } @mac_addrs; print for @a
+'
AP0015.faa3.f03a
AP0015.faa3.f03b
AP0015.faa3.f03c
- voilą!! you already have your answer. Now, package everything in a neat subroutine:
$ perl -le '
> use strict;
> my @mac_addrs = ("0015FAA3F03A", "0015FAA3F03B", "0015FAA3F03C");
> sub convert {
> map { "AP" . join ".", unpack "(A4)*", lc $_ } @_
> }
> print for convert @mac_addrs
> '
AP0015.faa3.f03a
AP0015.faa3.f03b
AP0015.faa3.f03c
Got it?
| [reply] [d/l] [select] |
|
|
print map uc($_), qw( a b c d ); # Prints ABCD
print map { uc($_) } qw( a b c d ); # Same thing
unpack '(A4)*', $val splits the value into as many 4 byte blocks as possible. I would have used "W" instead of "A", though. unpack '(W4)*', $val splits the value into as many 4 character blocks as possible.
@_ is not unamed —its name is @_— and nothing is being stored in it —its contents are being passed to map.
@_ holds the arguments of the current function (convert).
| [reply] [d/l] [select] |
|
|
|
|
|
|
|
|
|
|
|
|
|
"AP" . join "." - I understand what this is doing, but I don't understand from any documentation what the first argument to map is, generally speaking (everything before the first comma)
map this, that will do this to each element of that, with $_ being aliased to each of the elements
For example
my @in = 1 .. 5;
my @out = map $_ * 2, @in; # doubles each element of @in
@out = map $_ * 2, 1 .. 5; # same thing - any list will do
| [reply] [d/l] |