【问题标题】:How to print the last value of my output in perl?如何在 perl 中打印输出的最后一个值?
【发布时间】:2020-03-04 15:35:01
【问题描述】:

我编写了一个脚本,它第一次可以按我的意愿工作:P 我将在下面提供我的脚本。我唯一想做但没有成功的事情就是只打印输出的最后一个值。

use strict;
use warnings;
use feature 'say';

my $filename = '/home/Desktop/my_scripts/coordinates_based';

my $p;
my $i;
my $mikos = 0;
my $sum = 0;
my $count = 1;
my $mikos_thetika = 0;
my $mikos_arnitika = 0;
my $k = 1;
my @table = ();
my $final = 0;
my $neg = 0;
my $pos = 0;
my $size;
my $last_value = 0;

open(FH, '<', $filename) or die $!;

while (<FH>) {

    my ($n, $p, $a, $b) = split '\s+';

    chomp;
    $i =  1 if $p eq '+';
    $i = -1 if $p eq '-';

    for ($k..$n)
    {
        $mikos = $b - $a;
        if ($p eq '+')
        {
            $mikos_thetika = $mikos*1 ;
            $pos += $mikos_thetika;
        }
        else
        {
            $mikos_arnitika = $mikos*(-1);
            $neg += $mikos_arnitika;
        }

        $final = $pos + $neg;

        #chomp;
        #push(@table, $final);

        #$size = scalar @table;
        say $final;

        $k = $k + 1;
    }
}
close(FH);

脚本说明: 输出计算 $b 和 $a 之间的差异,并根据 $p 分别打印其与 - 或 + 的差异。最后打印这些差异的总和,但不是我想要的总和,而是作为前一个值与即将到来的值的总和($final)。

我试过这些:

1)  if ($k==$n) # $n is first column of my input file
    {$last_value =$table[$k];}
    say $last_value; # But it prints the same output.

2)  $size= scalar @table;
    $last_value = $table [$size];
    say $size; # prints the correct table size but not as value 
               # but as an entire sequence. For example 
               # if my table size is 5 it prints 1\n 2\n 3\n 4\n 5

3) # Also i tried the List library 
    use List::Util qw(sum);
    $last_value=sum($final);

输入文件:

$n $p $a $b 1 - 852 1934 2 - 2020 3108 3 + 3212 3781 4 - 3917 4162 5 - 4263 4421

脚本后的输出文件

-1082 -2170 -1601 -1846 -2004

期望的输出

2004年

【问题讨论】:

  • 我不确定您到底需要什么,但通常在循环之后立即打印感兴趣的东西就足够了,因此您可以从最后一次迭代中获得价值。 (另外,它说“在我的脚本之后输出文件”,但您显示的代码不会打印到文件中?)
  • split 有一个特殊的模式:split ' ', $str -- ' '(空格)表示\s+ @987654329开头的前导空格@ 已移除。非常便利!这也是它的默认设置,因此对于split ' ', $_,您可以只写split。这也有点成语,它完全适合你正在做的事情,所以my ($n, $p, $a, $b) = split;。 (注意这是有效的,因为while (...) 中没有定义变量,所以$_ 确实分配了&lt;FH&gt; 读取的内容。)请参阅split 的文档
  • 最好使用词法文件句柄open my $fh, ...。比 typeglobs(FH 等)更好
  • 在声明数组(或哈希)时,像my @ary = (); 这样的“空”初始化只不过是my @ary;。 (在程序中需要时清空数组很有用,比如@ary = ();,但那不一样。)
  • context 的概念在 Perl 中非常重要,它无处不在,而且非常流畅。示例:当您将数组分配给标量变量时,它是被分配的数组的大小,因为分配本身(= 运算符)位于 标量上下文 中。所以在$size = @table; 中,@table 的大小就是$size 得到的。所以绝对不需要scalar(代码中的注释行)。

标签: perl


【解决方案1】:

你基本上有这种情况,在每次迭代结束时 你输出一些东西。你一直这样做是因为你不知道什么时候会 到达最后一个元素。

while( <FH> ) {
    say $_ ** 2;
    }

不要输出一些东西,而是保存结果以供以后输出。什么时候 您遇到另一个元素,请替换该值。当你用完 输入,你记住的最后一个值是你想要的值 输出:

my $previous_output;
while( <FH> ) {
    $previous_output = $i ** 2;
    }

say $previous_output;

或者,您可以通过制作所有输出并使用tail(或类似名称)仅抓取最后一行来作弊:

% perl script.pl | tail -n 1

【讨论】:

  • 我明白你的意思。我也从@Zdim 得到了同样的答案。公平地说,我知道你提出的“作弊”,但由于我在编程语言方面的经验为零,我决定编写这个脚本以开始以某种方式学习:P。谢谢你的回答!!!
【解决方案2】:

好吧,我冒昧清理你的代码。

请查看您是否发现以下代码 sn-p 比您的原始代码更容易阅读/理解。

它的用途/功能与您的原始代码相同,除了在程序退出之前处理所有数据后根据您的请求输出最终结果。

注意: zdim 让我注意到代码可以改进 split 特殊情况,if ... else ... CPU 周期效率(用于净化的输入数据)和关于标头跳过代码(标头可以不存在或具有不同的格式)。虽然最初我并不打算改进代码,而只是可读性,但我认为zdim 的建议是值得的改变。

注意: 使用next unless /\d+ +[+-] +\d+ +\d+/; 清理的输入数据可能看起来有些不寻常,因为通常写为next unless /\d+\s+[+-]\s+\d+\s+\d+/; - 更多的是出于演示目的,可以通过不同方式实现相同的目标.当数据字段可以用多个空格分隔时,我假设输入数据的潜在情况不一致(用户可以调整间距以提高可读性)。 ' '\s 完全不同,因为首先假设仅 ' '(空格),但 '\s' 假设 [ \f\n\r\t]。 perlre

use strict;
use warnings;
use feature 'say';

my $filename = 'coordinates_based';    # I run code in same directory as input file

my($k,$pos,$neg,$final);               # only necessary variables for computation 

$k = 1;
$pos = $neg = 0;                       # some variables initialization

open my $fh, '<', $filename
    or die "Could not open $filename: $!";

while (<$fh>) {                             # walk through file
    next unless /\d+ +[+-] +\d+ +\d+/;      # sanitize input data
    chomp;

    my ($n, $p, $a, $b) = split;            # see split doc for special case

    for ($k..$n)
    {
        if ($p eq '+') {
            $pos += $b-$a;   # thetika  (positive)
        } else {
            $neg -= $b-$a;   # arnitika (negative)
        }

        $final = $pos + $neg;

        $k++;
    }
}

close $fh;

say $final;                            # output result of computation

输出

-2004

【讨论】:

  • 既然声明的目的是清理代码(很好!),让我评论一下。 (1) split '\s+'; 应该使用特殊模式' ' 而不是\s+,因为这是默认模式,所以它是... = split;。也是成语。 (2) 两条语句测试$p的独占值;它应该是一个if-else 分支。 (3)如果你决定压缩他们的$k = $k + 1(我会说是有道理的)那为什么不一路走,++$k;。我根本没有考虑逻辑。 (这些只是善意的 cmets,也不是批评。)
  • 另外——测试每一行的标题都需要注释。如果在循环之前删除了头部(void &lt;$fh&gt;),这很常见,那么当文件没有头部时当然会出现问题:如何测试我们何时需要先读取? (很容易解决,但会使代码复杂化。)但是,我想说这绝对需要评论和考虑,因为通常知道处理的数据文件是否有标题行。 (另外,如果我们甚至不知道是否有标题行,那么针对 /\$n/ 的测试也可能不是很确定,所以我们又回到了不确定状态。)
  • @zdim -- 我的清理肉是为了提高可读性而不是代码改进。我同意++$k$k++。如果 OP 保证输入文件将保持在示例中,那么if ... else ... 将起作用(它节省了几个 CPU 周期,但不多)。我想过用next unless /\d\s+[+-]\s+\d+\s+\d+/;净化输入数据。我保留了\s+,以防万一输入数据因错误而有多个 ` `(空格)分隔字段(从 OP 的原始代码中保留)。如果 OP 可以保证输入格式,那么 next unless /\d [+-] \d+ \d+/ (/\d +[+-]+ +\d +\d/) will do nicely as will split ' '`.
  • @zdim -- 可能出现的主要问题是输入数据的不一致,人们往往会无意中犯错误。根据终端有时难以区分是否有一个或两个空格。更成问题的是,一些代码用户会决定通过添加额外的空格来调整列中的数字以提高可读性。这将使读取数据文件更容易,但正则表达式不考虑它(\s+ 用于这种情况,因为它更灵活)。
  • 抱歉没有说明,如果您不知道这种特殊模式,我假设您会查看 split 的文档——' '\s+,并且还删除了前导空格
猜你喜欢
  • 2017-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-15
相关资源
最近更新 更多