我正在尝试实现一个以数组作为参数的子程序(或使用多个参数 - 仍然没有完全区分),并返回true或false,具体取决于是否array是一个递增的序列(每个数字必须比最后一个数字多1):
isIncreasingArray(1,2,3,4); # true
isIncreasingArray(1,2,3,1); # false
isIncreasingArray(0,9,1); # false
isIncreasingArray(-2,-1,0); # true
isIncreasingArray(1,1,1,1); # false
这就是我想出来的:
sub isIncreasingArray {
my $last;
foreach $n (@_) {
return 0 if defined($last) && $last != $n - 1;
$last = int($n);
}
return 1;
}
我对Perl很陌生,我想知道是否有更简单或更简洁的方法来实现这一目标?另外,这是我根据最佳实践编写的内容吗?
答案 0 :(得分:16)
有几点:
为了提高效率,特别是为了最小化内存占用,您可能希望将对数组的引用传递给子例程。
在列表上下文中,return 0
将返回由单个元素组成的列表,因此将为true。如果您想要返回return
并在所有情境中完成工作,则只需false
。
通过比较第一个和最后一个,第二个和第二个等的差异,可能会将比较的数量减少一半,以查看差异在索引中的差异,但我不是在考虑那个现在很清楚。
这是一个基于你的略有不同的版本。请注意,您应该使用strict
并确保使用my
#!/usr/bin/env perl
use strict; use warnings;
use Carp qw(croak);
use Test::More;
ok( isSimplyIncreasingSequence( [ 1298 ] ) ); # true
ok( isSimplyIncreasingSequence( [1,2,3,4] ) ); # true
ok( not isSimplyIncreasingSequence( [1,2,3,1] ) ); # false
ok( not isSimplyIncreasingSequence( [0,9,1] ) ); # false
ok( isSimplyIncreasingSequence( [-2,-1,0] ) ); # true
ok( not isSimplyIncreasingSequence( [1,1,1,1] ) ); # false
done_testing();
sub isSimplyIncreasingSequence {
my ($seq) = @_;
unless (defined($seq)
and ('ARRAY' eq ref $seq)) {
croak 'Expecting a reference to an array as first argument';
}
return 1 if @$seq < 2;
my $first = $seq->[0];
for my $n (1 .. $#$seq) {
return unless $seq->[$n] == $first + $n;
}
return 1;
}
当然还有一些基准:
#!/usr/bin/env perl
use strict; use warnings;
use Benchmark qw( cmpthese );
use Carp qw( croak );
my %cases = (
ordered_large => [1 .. 1_000_000],
ordered_small => [1 .. 10],
unordered_large_beg => [5, 1 .. 999_000],
unordered_large_mid => [1 .. 500_000, 5, 500_002 .. 1_000_000],
unordered_large_end => [1 .. 999_999, 5],
);
for my $case (keys %cases) {
print "=== Case: $case\n";
my $seq = $cases{$case};
cmpthese -3, {
'ref' => sub { isSimplyIncreasingSequence($seq) },
'flat' => sub {isIncreasingArray(@{ $seq } ) },
};
}
sub isSimplyIncreasingSequence {
my ($seq) = @_;
unless (defined($seq)
and ('ARRAY' eq ref $seq)) {
croak 'Expecting a reference to an array as first argument';
}
return 1 if @$seq < 2;
my $first = $seq->[0];
for my $n (1 .. $#$seq) {
return unless $seq->[$n] == $first + $n;
}
return 1;
}
sub isIncreasingArray {
my $last;
foreach my $n (@_) {
return 0 if defined($last) && $last != $n - 1;
$last = int($n);
}
return 1;
}
=== Case: unordered_large_mid Rate flat ref flat 4.64/s -- -18% ref 5.67/s 22% -- === Case: ordered_small Rate ref flat ref 154202/s -- -11% flat 173063/s 12% -- === Case: ordered_large Rate flat ref flat 2.41/s -- -13% ref 2.78/s 15% -- === Case: unordered_large_beg Rate flat ref flat 54.2/s -- -83% ref 315/s 481% -- === Case: unordered_large_end Rate flat ref flat 2.41/s -- -12% ref 2.74/s 14% --
答案 1 :(得分:9)
为什么没有人提出智能匹配解决方案?
虽然这个解决方案不如其他一些解决方案那么高效,但它还具有使用字符串的额外好处。
修改强>
对于空元素列表和单元素列表,Sub现在返回true,因为that's what the experts say it should do:
use strict;
use warnings;
use 5.010;
sub is_simply_increasing { @_ < 2 || @_ ~~ [$_[0] .. $_[-1]] }
say ( is_simply_increasing(1,2,3,4) ? 'true' : 'false' ); # true
say ( is_simply_increasing(1,2,3,1) ? 'true' : 'false' ); # false
say ( is_simply_increasing(0,9,1) ? 'true' : 'false' ); # false
say ( is_simply_increasing(-2,-1,0) ? 'true' : 'false' ); # true
say ( is_simply_increasing(1,1,1,1) ? 'true' : 'false' ); # false
say ( is_simply_increasing(1,4,1,-1) ? 'true' : 'false' ); # false
say ( is_simply_increasing('a','c') ? 'true' : 'false' ); # false
say ( is_simply_increasing('love'..'perl') ? 'true' : 'false' ); # true
say ( is_simply_increasing(2) ? 'true' : 'false' ); # true
say ( is_simply_increasing() ? 'true' : 'false' ); # true
我喜欢它,当我的子线是单线!
答案 2 :(得分:8)
我最终得到的东西比你的长一点。我想,这意味着您的解决方案没有任何问题:)
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Test::More;
sub is_increasing_array {
return unless @_;
return 1 if @_ == 1;
foreach (1 .. $#_) {
return if $_[$_] != $_[$_ - 1] + 1;
}
return 1;
}
ok(is_increasing_array(1,2,3,4)); # true
ok(!is_increasing_array(1,2,3,1)); # false
ok(!is_increasing_array(0,9,1)); # false
ok(is_increasing_array(-2,-1,0)); # true
ok(!is_increasing_array(1,1,1,1)); # false
done_testing;
答案 3 :(得分:6)
使用前6“交叉点”:
sub is_increasing_list {
use List::MoreUtils qw<none>;
my $a = shift;
return none {
( my $v, $a ) = (( $_ - $a != 1 ), $_ );
$v;
} @_;
}
none
表达式也可以(更隐蔽地)写为
return none { [ ( $a, undef ) = ( $_, ( $_ - $a - 1 )) ]->[-1]; } @_;
(如果约束是$ x [$ n + 1] - $ x [$ n] == 1,那么减去1也会产生“Perl真值条件”。)
实际上认为它是一个'无'的结点运算符是一种落后的概念,所以我将使用all
:
sub is_increasing_list {
use List::MoreUtils qw<all>;
my $a = shift;
return all { [ ( $a, undef ) = ( $_, ( $_ - $a == 1 )) ]->[-1]; } @_;
}
答案 4 :(得分:5)
有人必须在这里投入功能编程解决方案,因为这种数学公式只需要递归。 ;)
sub isIncreasingArray {
return 1 if @_ <= 1;
return (pop(@_) - $_[-1] == 1) && isIncreasingArray(@_);
}
对于作为数组与多个参数的子例程参数,请按照这种方式考虑:Perl总是向子例程发送一个参数列表作为数组@_。您可以将该数组中的参数作为单个标量移位或弹出,或者以整数列表作为数组运行。从你的子程序里面,它仍然是一个数组,句号。
如果您进入引用,是的,您可以将引用到数组传递到子例程。该引用在技术上仍然作为包含一个标量值的数组(列表)传递给子例程:引用。首先,我忽略了所有这些,并在没有参考的情况下围绕基本操作。
调用子程序。这样,Perl就会秘密地将你的scalars列表转换为一系列标量:
isIncreasingArray(1,2,3,4);
这样,Perl就会传递你的数组:
@a = (1,2,3,4);
$answer = isIncreasingArray(@a);
无论哪种方式,子程序都会得到一个数组。它是一个副本*,因此在这里引用效率。不要担心,对于K <10,000,即使这里我的效率低,学术性,优雅,递归的解决方案,我的笔记本电脑仍然需要不到1秒的时间:
print isIncreasingArray(1..10000), "\n"; # true
*副本:有点但不是真的吗?请参阅下面的评论和其他资源,例如PerlMonks。 “有人可能会说Perl总是通过引用,但保护我们自己。”有时。实际上,我在子程序中创建自己的副本到本地化的“我的”变量。就这样做。
答案 5 :(得分:4)
这是我可以达到的最短形式,检查地图中的每个元素以查看它是否等于增加的自我,返回一组0和1,计数1并匹配原始大小集。
print isIncreasingArray(1,2,3),"\n";
print isIncreasingArray(1,2,1),"\n";
print isIncreasingArray(1,2),"\n";
print isIncreasingArray(1),"\n";
sub isIncreasingArray {
$i = $_[0];
(scalar grep { 1 == $_ } map { $i++ == $_ } @_) == scalar(@_) || 0;
}
答案 6 :(得分:3)
无论你使用什么实现,事先做一些快速检查都没有坏处:
sub isSimplyIncreasingSequence {
return 1 if @_ < 2;
return 0 if $_[-1] - $_[0] != $#_;
...
}