改变元素在Perl中的位置

时间:2017-03-21 00:21:45

标签: arrays perl

所以我遇到了问题,我无法解决。如果我从Perl中的文件中读取一些单词,在该文件中单词不是按顺序排列,而是有一个数字(作为第一个字符)应该是元素构成句子的位置。 0表示位置正确,1表示该单词应位于[1]等位置。 该文件如下所示: 0This 3a 4sentence 2be 1should ,解决方案应该类似于 0这1应该是3a 4sentence

for循环中,我浏览了从文件中获取的单词数组,这就是我获取第一个字符(数字)$firstCharacter = substr $words[$i], 0, 1;的方式,但我不知道知道如何正确更改数组。

这是我使用的代码

#!/usr/bin/perl -w
$arg = $ARGV[0];
open FILE, "< $arg" or die "Can't open file: $!\n";
$/ = ".\n";
while($row = <FILE>)
{
    chomp $row;
    @words = split(' ',$row);
}
for($i = 0; $i < scalar @words; $i++)
{
    $firstCharacter = substr $words[$i], 0, 1;
    if($firstCharacter != 0)
    {

    }
}

3 个答案:

答案 0 :(得分:7)

只需使用sort即可。您可以使用列表上下文中的匹配来提取数字,使用\d+即使数字&gt;也可以使用9:

#! /usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

my @words = qw( 0This 3a 4sentence 2be 1should );
say join ' ', sort { ($a =~ /\d+/g)[0] <=> ($b =~ /\d+/g)[0] } @words;

如果您不介意警告,或者您愿意将其关闭,您可以直接对单词进行数字比较,Perl将自行提取数字前缀:

no warnings 'numeric';
say join ' ', sort { $a <=> $b } @words;

答案 1 :(得分:3)

假设您有一个这样的数组:

my @words = ('0This', '3a', '4sentence', '2be', '1should');

你希望它像这样排序:

('0This', '1should', '2be', '3a', '4sentence');

这有两个步骤。首先是提取前导号码。然后按那个数字排序。

您无法使用substr,因为您不知道该数字可能有多长。例如,('9Second', '12345First')。如果您只查看第一个字符,则会得到9和1,并对它们进行错误排序。

相反,您可以使用正则表达式来捕获数字。

my($num) = $word =~ /^(\d+)/;

有关其工作原理的详情,请参阅perlretut,尤其是documentation

既然您可以捕获这些数字,那么他们可以Extracting Matchessort不是自己循环,而是为您处理排序。您所要做的就是提供排序标准。在这种情况下,我们从每个单词中捕获数字(按排序分配给$ a和$ b)并将它们作为数字进行比较。

@words = sort {
    # Capture the number from each word.
    my($anum) = $a =~ /^(\d+)/;
    my($bnum) = $b =~ /^(\d+)/;

    # Compare the numbers.
    $anum <=> $bnum
} @words;

有多种方法可以提高效率,尤其是sort

你也可以作弊。

如果你要求Perl将某些东西视为一个数字,那么它将最大程度地遵守。如果字符串以数字开头,它将使用它并忽略其余部分,但它会抱怨。

$ perl -wle 'print "23foo" + "42bar"'
Argument "42bar" isn't numeric in addition (+) at -e line 1.
Argument "23foo" isn't numeric in addition (+) at -e line 1.
65

我们可以通过直接将单词作为数字进行比较来利用它来简化排序。

{
    no warnings 'numeric';
    @words = sort { $a <=> $b } @words;
}

请注意,我关闭了将单词用作数字的警告。 use warningsno warnings仅在当前区块中生效,因此将no warnings 'numeric'sort放在自己的区块中我只关闭了该区域的警告排序声明。

最后,如果单词在文件中,您可以从命令行使用Unix sort实用程序。使用-n进行“数字排序”,它将执行与上述相同的操作。

$ cat test.data
00This
3a
123sentence
2be
1should

$ sort -n test.data
00This
1should
2be
3a
123sentence

答案 2 :(得分:1)

您应该可以拆分空格,这将使数字成为单词的第一个字符。有了这个假设,你可以简单地使用数字比较运算符(<=>)进行比较而不是字符串比较(cmp)。

运算符非常重要,因为如果比较字符串,则会使用第一个字符,这意味着101112会出现故障,并列在{{{}附近1}}(1代替1,10,11,12,2,3,4…)。

拆分,然后排序

注意: @schwern评论了一个重点。如果你使用警告 - 你应该 - 你会收到警告。这是因为内部比较变量1,2,3,4…10,11,12$a的值不是数字,而是字符串(例如,`&#34; 0这个&#34;,&#34; 3a& #34)。我已更新以下键盘并提供了更合适的替代方法以避免此问题。

http://codepad.org/xs2GH9xT

$b

替代

  1. 一种方法是使用use strict; use warnings; my $line = q{0This 3a 4sentence 2be 1should}; my @words = split /\s/,$line; my @sorted = sort {$a <=> $b} @words; print qq{ Line: $line Words: @words Sorted: @sorted }; 中的no warnings 'numeric'来忽略警告。如他所示,关闭一个区块中的警告将在之后重新启用它,与Schwern's answer相比可能有点万无一失,后者将其应用于更广泛的范围。

  2. Choroba's answer通过在内部解析这些值中的数字来工作。代码行数要少得多,但出于性能原因,我通常会反对这一点。正则表达式不是每个单词只运行一次,而是在排序过程中多次运行。

  3. 另一种方法是删除数字并将其用于排序比较。我尝试通过创建哈希来执行此操作,其中键将是数字,值将是单词。

  4. 哈希映射/密钥排序

    一旦你有一个数组,其值是以数字为前缀的单词,你可以很容易地将这些数字/单词组合分成一个散列,其中键作为数字和值作为单词。这是通过使用split完成的。

    关于split语句的重要注意事项是传递 limit (在本例中为2),这限制了字符串的最大字段数分成。

    然后在map中使用这两个值来构建键/值赋值。因此,"0This"分为"0""This",以便在哈希中使用"0"=>"This"

    Choroba's solution

    use strict;
    use warnings;
    
    my $line   = q{0This 3a 4sentence 2be 1should};
    my @words  = split /\s/, $line;                            # [ '0This', '3a', ... ]
    my %mapped = map { split /(?=\D)/, $_, 2 } @words;         # { '0'=>'This, '3'=>'a', ... }
    my @sorted = @mapped{ sort { $a <=> $b } keys %mapped };   # [ 'This', 'should', 'be', ... ]
    
    print qq{
      Line: $line
      Words: @words
      Sorted: @sorted
    };
    

    这也可以进一步优化,但使用多个变量来说明过程中的步骤。