以下代码可以满足我的需求。它打印列表并在不连续的行的末尾添加星号,例如如果你从1到3或3到5跳过。
use strict;
use warnings;
#note: thanks to all who helped with formatting issues.
#note: I recognize a hash would be a much better option for what I want to do.
my @printy = ("1 -> this",
"5 -> that",
"3 -> the other",
"6 -> thus and such");
@printy = sort {num($a) <=> num($b)} @printy;
my $thisID = 0;
my $lastID = 0;
#print out (line)* if initial number is >1 more than previous, or just (line) otherwise
for (@printy)
{
$thisID = $_; $thisID =~s/ .*//g;
if ($thisID - $lastID != 1) { $_ =~ s/$/ \*/; }
$lastID = $thisID;
}
print join("\n", @printy) . "\n";
sub num
{
my $x = $_[0];
$x =~ s/ .*//;
return $x;
}
但我认为我可以做得更好。感觉很纠结,我的直觉告诉我,我错过了一些能够更容易完成工作的强大功能,可能需要两行。
现在我以前使用过map()
命令,但只是查看/修改元素,而不是它与前一个元素的比较。任何人都可以推荐一种方法来使这更简洁吗?谢谢!
答案 0 :(得分:7)
由于Perl推广TIMTOWTDI,map
起初可能看起来很有吸引力。让我们看看它如何为这项任务付出代价:
由于需要访问相邻元素,因此使用索引很方便。由于对于n
个元素,有n-1
个邻居对,因此您不必循环n
次。在这种情况下,让我们从1
开始,而不是通常的0
:
1 .. $#printy
可以通过调用map
内的相关索引来访问相邻元素。
map { my $prev = $printy[$_-1]; my $curr = $printy[$_] } 1 .. $#printy;
数组切片更简洁地表达了这一点:
map { my ( $prev, $curr ) = @printy[$_-1,$_]; } 1 .. $#printy;
让我们介绍使用num
子程序比较数字的真实逻辑:
map {
my ( $prev, $curr ) = @printy[$_-1,$_];
if ( num($curr) - num($prev) > 1 ) {
"$curr *";
}
else {
$curr;
}
} 1 .. $#printy;
相当于:
map {
my ( $prev, $curr ) = @printy[$_-1,$_];
$curr .= " *" if num($curr) - num($prev) > 1;
$curr
} 1 .. $#printy;
记住不要忘记第一个元素:
@printy = ( $printy[0],
map {
my ( $prev, $curr ) = @printy[$_-1,$_];
$curr .= " *" if num($curr) - num($prev) > 1;
$curr
} 1 .. $#printy
);
鉴于最终结果,我不太确定我会使用map
:
答案 1 :(得分:4)
不需要地图,只需在此处添加一些空格,并删除不需要的内容($ _,加入等)。此外,在循环内重用num()
,无需重复正则表达式。
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
my @printy = sort { num($a) <=> num($b) }
'1 -> this', '5 -> that', '3 -> the other', '6 -> thus and such';
my $thisID = my $lastID = 0;
for (@printy) {
$thisID = num($_);
$_ .= ' *' if $thisID - $lastID != 1;
$lastID = $thisID;
}
say for @printy;
sub num {
my ($x) = @_;
$x =~ s/ .*//;
return $x;
}
此外,使用num
而不是替换重新实现/(\d+)/
可能会更清楚地说明其目的。
答案 2 :(得分:3)
我同意choroba认为这里不需要map
。但无论如何我还要重构一下。
use strict;
use warnings;
use feature 'say';
my @printy = ( "1 -> this", "5 -> that", "3 -> the other", "6 -> thus and such" );
my $last_id = 0;
foreach my $line ( sort { num($a) <=> num($b) } @printy ) {
my $current_id = num($line);
$line .= ' *' unless $current_id - $last_id == 1;
$last_id = $current_id;
}
say for @printy;
# returns the number at the start of a string
sub num {
$_[0] =~ m/^(\d+)/;
return $1;
}
我将sort
向下移动到foreach
,因为您不应该依赖于输入已排序的事实。
我更改了变量名称以符合变量名称中不应该包含大写字母的约定,我使用了say
,就像print
一样,系统特定的换行符最后。
我还将$current_id
移到了循环中。这并不需要在外面可见,因为它是该循环的词汇。始终在尽可能小的范围内声明变量。
您已经拥有了很好的num
功能,但是您并未在循环内使用它来获取$current_id
。使用它。
我认为如果输入变得很长,那么使用map
构造可能是有意义的,因为在某些时候排序会非常昂贵。在排序之前,请查看Schwartzian transform以缓存计算。然后你可以一次完成所有事情。但它不再适合初学者。
答案 3 :(得分:3)
您的数据大喊“#34;使用哈希!&#34;对我来说。
如果我们有哈希,
my %printy =
map { split / -> / }
"1 -> this", "5 -> that", "3 -> the other", "6 -> thus and such";
解决方案就是:
my @order = sort { $a <=> $b } keys(%printy);
for my $i (@order[1..$#order]) {
$printy{$i} .= ' *'
if !exists($printy{$i-1});
}
print "$_ -> $printy{$_}\n"
for @order;
虽然我不确定它是否物有所值,但可以打高尔夫球。
my $count;
print "$_ -> $printy{$_}".( !$count++ || exists($printy{$_-1}) ? "" : " *" )."\n"
for
sort { $a <=> $b }
keys(%printy);
for
可以转换为map
,但效率会降低。
my $count;
print
map { "$_ -> $printy{$_}".( !$count++ || exists($printy{$_-1}) ? "" : " *" )."\n" }
sort { $a <=> $b }
keys(%printy);
答案 4 :(得分:2)
我还建议清理代码并保持循环。但是,这是基于map
的方式。
代码使用已排序的@printy
和num
子。
my @nums = map { num($_) } @printy;
my @res = map {
$nums[$_] == $nums[$_-1] + 1 # invariably false for $_ == 0
? $printy[$_] : $printy[$_] .= ' *';
}
(0..$#printy);
say for @res;
这适用于第一个元素,因为它不是在最后一个元素之后,因为我们已经排序了。虽然这可能有点恶魔,但它需要在代码中进行评论。所以也许更好地拼出来
my @res = map {
($nums[$_] == $nums[$_-1] + 1) ? $printy[$_] : $printy[$_] .= ' *';
}
(1..$#printy);
unshift @res, $printy[0];
不是很干净但很清楚。
当然,与直线循环相比,所有这些都有额外的功效。
答案 5 :(得分:0)
对不起,但您的代码很乱,而且您需要做的远不止使用map
来清理此代码
你在一行上有 no indentation 和多个语句,而你没有考虑过你的逻辑。您的代码无法维护
以下是我写这个的方法。它构建一个并行的ID数组,然后对索引列表进行排序,以便ID和原始数据都按顺序排列
如果它让你更开心,它确实包括map
use strict;
use warnings 'all';
my @data = ( '1 -> this', '5 -> that', '3 -> the other', '6 -> thus and such' );
my @ids = map { /(\d+)/ } @data;
my @indexes = sort { $ids[$a] <=> $ids[$b] } 0 .. $#ids;
my $last_id;
for my $i ( @indexes ) {
print $data[$i];
print ' *' if defined $last_id and $ids[$i] > $last_id + 1;
print "\n";
$last_id = $ids[$i];
}
1 -> this
3 -> the other *
5 -> that *
6 -> thus and such