【问题标题】:Smart formatting of print output打印输出的智能格式化
【发布时间】:2020-05-12 23:01:01
【问题描述】:

你好,

作为 StackExchange 的新用户,我想大胆地开始贡献
问 Perl 问题。

在 4 名玩家之间处理 52 张标准套牌的脚本中 [1]
打印卡片的部分需要一些修正。

现在我将 W 面和 E 面分别打印在不同的行中:

♠ J 9 7 6                                                                       
♥ K                                                                             
♦ K J 9 3 2                                                                     
♣ A 3 2                                                                         
                                                ♠ A K 5 3                       
                                                ♥ Q                             
                                                ♦ 8 7 6 5                       
                                                ♣ 9 6 5 4                       

但它们应该在同一行:

♠ J 9 7 6                                       ♠ A K 5 3                       
♥ K                                             ♥ Q                             
♦ K J 9 3 2                                     ♦ 8 7 6 5                       
♣ A 3 2                                         ♣ 9 6 5 4                       

所以E面的每个颜色(西装)符号都印在同一行
W 方对应的花色符号并在同一列中
E方剩余的花色符号。

W和E对应的西装是用print打印出来的:

# last suit (clubs: A 3 2) of W side                                            
print "\x{2663} "; print $deck{$_}," " foreach (sort {$a <=> $b} @wc); print "\n";
# first suit (spades: A K 5 3) of E side                                        
print "\t\t\t\t\t\t\x{2660} "; print $deck{$_}," " foreach (sort {$a <=> $b} @es); print "\n";

考虑到 W 侧花色长度可能会因洗牌而有所不同,看来
对我来说,应该避免使用标签或将标签换成其他东西来打印 E 面。
那怎么打印E面呢?

[1]Script source

【问题讨论】:

  • 好吧,你必须重新排列你的打印件(因此可能要重新组织代码)---在第一个 J 9 7 6 之后不要打印换行符,而是留一些空间然后打印 A K 5 3,然后是换行符。等等。请注意,sprintf 是您的朋友。 (然后还有用于打印表格布局的模块。)
  • 另见Perl6::Form
  • @zdim:感谢您的建议。该脚本现在可以正确打印。我使用了: - printf 打印从 for 循环中获得的数组元素,根据 link - 和 printf 功能 %*s 启用动态设置字段宽度,具体取决于按照 link 组合的所有数组元素的长度。
  • 很高兴听到这个消息 :) sprintf 很值得了解(对于格式/表示转换也是如此)。然后你从戴夫那里得到了很好的答案:)

标签: perl


【解决方案1】:

最简单的方法是交错打印东手和西手的代码行(并从每对中的第一行 print() 中删除换行符)。

print "\x{2660} "; print $deck{$_}," " foreach (sort {$a <=> $b} @ws);
print "\t\t\t\t\x{2660} "; print $deck{$_}," " foreach (sort {$a <=> $b} @es); print "\n";
print "\x{2665} "; print $deck{$_}," " foreach (sort {$a <=> $b} @wh);
print "\t\t\t\t\x{2665} "; print $deck{$_}," " foreach (sort {$a <=> $b} @eh); print "\n";
print "\x{2666} "; print $deck{$_}," " foreach (sort {$a <=> $b} @wd);
print "\t\t\t\t\x{2666} "; print $deck{$_}," " foreach (sort {$a <=> $b} @ed); print "\n";
print "\x{2663} "; print $deck{$_}," " foreach (sort {$a <=> $b} @wc);
print "\t\t\t\t\x{2663} "; print $deck{$_}," " foreach (sort {$a <=> $b} @ec); print "\n";

第一次运行时,我得到了很好地说明问题的结果:

♠ 9 3 2                 ♠ A K Q J 7 6 
♥ 9 6 5 4               ♥ Q 7 
♦ 10 8              ♦ A 3 
♣ 9 8 3 2               ♣ K 10 5 

因为西方只有两颗心,所以东方的心印在左边太远了。我们可以解决这个问题,但最好先简化一下代码。让我们编写一个名为suit_hand() 的子例程,它接受您的一张卡片数组并返回我们需要打印的字符串。

sub suit_hand {
  my $symbol = shift;
  my @positions = sort { $a <=> $b} @_;
  my @cards = map { $deck{$_} } @positions;
  return "$symbol " . join ' ', @cards;
}

然后我们可以从您的程序中删除大量重复代码。

print "\t\t\t", suit_hand("\x{2660}", @ns), "\n";
print "\t\t\t", suit_hand("\x{2665}", @nh), "\n";
print "\t\t\t", suit_hand("\x{2666}", @nd), "\n";
print "\t\t\t", suit_hand("\x{2663}", @nc), "\n";
print suit_hand("\x{2660}", @ws), "\t\t\t\t", suit_hand("\x{2660}", @es), "\n";
print suit_hand("\x{2665}", @wh), "\t\t\t\t", suit_hand("\x{2665}", @eh), "\n";
print suit_hand("\x{2666}", @wd), "\t\t\t\t", suit_hand("\x{2666}", @ed), "\n";
print suit_hand("\x{2663}", @wc), "\t\t\t\t", suit_hand("\x{2663}", @ec), "\n";
print "\t\t\t", suit_hand("\x{2660}", @ss), "\n";
print "\t\t\t", suit_hand("\x{2665}", @sh), "\n";
print "\t\t\t", suit_hand("\x{2666}", @sd), "\n";
print "\t\t\t", suit_hand("\x{2663}", @sc), "\n";

输出中没有任何变化,我们只是让代码更易于使用。

所以中间的四行是打印东西方指针的行。这就是我们当前的问题所在。准确地说,是这些行中间的一串制表符导致了问题。我们希望摆脱这些并用更好的格式化输出的方式替换它们。

这就是printf() 给我们的。这是一个“格式化的print()”函数。我们可以这样称呼它:

printf "%-40s %s\n", suit_hand("\x{2660}", @ws), suit_hand("\x{2660}", @es);

第一个参数 ("%-40s %s\n") 是格式字符串。 %s 表示“在此处插入一个字符串”,第一个字符串位置的-40 表示“将此字符串格式化为左对齐且长度为 40 个字符”——这意味着我们的第二个字符串将始终从第 41 个位置开始。

在格式字符串之后,第二个和第三个参数就是我们想要插入到输出中的字符串。在本例中,这只是我们两次调用 suit_hand() 返回的字符串。

所以我们的代码变成了这样:

print "\t\t\t", suit_hand("\x{2660}", @ns), "\n";
print "\t\t\t", suit_hand("\x{2665}", @nh), "\n";
print "\t\t\t", suit_hand("\x{2666}", @nd), "\n";
print "\t\t\t", suit_hand("\x{2663}", @nc), "\n";
printf "%-40s %s\n", suit_hand("\x{2660}", @ws), suit_hand("\x{2660}", @es);
printf "%-40s %s\n", suit_hand("\x{2665}", @wh), suit_hand("\x{2665}", @eh);
printf "%-40s %s\n", suit_hand("\x{2666}", @wd), suit_hand("\x{2666}", @ed);
printf "%-40s %s\n", suit_hand("\x{2663}", @wc), suit_hand("\x{2663}", @ec);
print "\t\t\t", suit_hand("\x{2660}", @ss), "\n";
print "\t\t\t", suit_hand("\x{2665}", @sh), "\n";
print "\t\t\t", suit_hand("\x{2666}", @sd), "\n";
print "\t\t\t", suit_hand("\x{2663}", @sc), "\n";

这是一些示例输出:

                    ♠ 8 5
                    ♥ 5
                    ♦ K Q 9 7 5
                    ♣ Q 8 4 3 2
♠ K Q 10 9                               ♠ A 4
♥ 10 9 8 3                               ♥ Q 4 2
♦ A J 8 4                                ♦ 6 3 2
♣ A                                      ♣ K 10 9 7 6
                    ♠ J 7 6 3 2
                    ♥ A K J 7 6
                    ♦ 10
                    ♣ J 5

请注意,这样做是正确的,即使韦斯特手里只有一把铁锹。

显然,您可以将 printf() 调用中的数字 40 更改为您认为最合适的任何值。

更新:因为午餐有点无聊,我写了一个你的程序版本,展示了我将如何解决这个问题:

#!/usr/bin/perl

use strict;
use warnings;

use List::Util 'shuffle';

binmode STDOUT, ':utf8';

my @suits = qw[spades hearts diamonds clubs];

my %suit = (
  spades   => "\x{2660}",
  hearts   => "\x{2665}",
  diamonds => "\x{2666}",
  clubs    => "\x{2663}",
);

my @deck;
for my $s (keys %suit) {
  for my $c (2 .. 14) {
    push @deck, [$s, $c];
  }
}

@deck = shuffle @deck;

my %hand;

for (1 .. 13) {
  for my $p (qw[s w n e]) {
    my $card = shift @deck;

    push @{ $hand{$p}{$card->[0]} }, $card->[1];
  }
}

show1hand($hand{n});
show2hands($hand{w}, $hand{e});
show1hand($hand{s});

sub show1hand {
  my $hand = shift;

  for (@suits) {
    print "\t\t\t", suit_hand($_, $hand->{$_}), "\n";
  }
}

sub show2hands {
  my ($hand1, $hand2) = @_;

  for (@suits) {
    printf "%-40s %s\n",
      suit_hand($_, $hand1->{$_}),
      suit_hand($_, $hand2->{$_});
  }
}

sub suit_hand {
  my ($suit, $cards) = @_;

  my %display = (11 => 'J', 12 => 'Q', 13 => 'K', 14 => 'A');

  my $card_str = join ' ',
                 map { $display{$_} // $_ }
                 sort { $b <=> $a } @$cards;

  return "$suit{$suit} $card_str";
}

【讨论】:

  • 惊人的答案,我对解决问题的教育方法和解释例如的用法表示赞赏。 printf。我上传了新版本,printf 的用法略有不同,请参阅我对问题的评论。该解决方案的午餐版本使我的代码看起来像是幼儿园制作的。我可能需要一些时间才能理解:push @{ $hand{$p}{$card-&gt;[0]} }... 构造,希望不会太多。戴夫,我可以将您的版本复制到我的存储库中,或者至少提及一下吗?
  • @mtth:请随意以任何你喜欢的方式使用我的代码。我通过阅读 perldoc perldsc 了解了此类复杂的数据结构 - 您可能会发现它也很有用。
  • 感谢您指向 perldsc - 我研究了有关多级数据结构的文档。我使用脚本 link 更新了 repo,还更新了 printf 的格式,因此每只手都有最小固定宽度的列,能够容纳尽可能长的花色(13 张牌)。 undefined value as an ARRAY reference 还有一个小缺陷,以防套装无效。请检查我的修复是否正确。
  • @mtth:你的修复对我来说看起来不错。
猜你喜欢
  • 2017-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多