我正在尝试实现一个子程序,该子程序将数组作为其参数(或使用多个参数 - 仍然没有完全区分),并返回true或false,具体取决于该数组是否为递增序列(每个数字必须为比上一个多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
Run Code Online (Sandbox Code Playgroud)
这就是我想出来的:
sub isIncreasingArray {
my $last;
foreach $n (@_) {
return 0 if defined($last) && $last != $n - 1;
$last = int($n);
}
return 1;
}
Run Code Online (Sandbox Code Playgroud)
我对Perl很新,我想知道是否有更简单或更简洁的方法来实现这一目标?另外,我是根据最佳实践编写的吗?
Sin*_*nür 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;
}
Run Code Online (Sandbox Code Playgroud)
当然还有一些基准:
#!/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;
}
Run Code Online (Sandbox Code Playgroud)
=== 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% --
为什么没有人提出智能匹配解决方案?
虽然这个解决方案不如其他一些解决方案那么高效,但它还具有使用字符串的额外好处.
编辑
Sub现在为空元素和单元素列表返回true,因为这是专家们应该做的:
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
Run Code Online (Sandbox Code Playgroud)
我喜欢它,当我的子线是单线!
我最终得到的东西比你的长一点.我想,这意味着您的解决方案没有任何问题:)
#!/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;
Run Code Online (Sandbox Code Playgroud)
使用前6"交叉点":
sub is_increasing_list {
use List::MoreUtils qw<none>;
my $a = shift;
return none {
( my $v, $a ) = (( $_ - $a != 1 ), $_ );
$v;
} @_;
}
Run Code Online (Sandbox Code Playgroud)
的none
表达也可以被写入(更含糊)作为
return none { [ ( $a, undef ) = ( $_, ( $_ - $a - 1 )) ]->[-1]; } @_;
Run Code Online (Sandbox Code Playgroud)
(如果约束是$ 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]; } @_;
}
Run Code Online (Sandbox Code Playgroud)
有人必须在这里投入功能编程解决方案,因为这种数学公式只需要递归.;)
sub isIncreasingArray {
return 1 if @_ <= 1;
return (pop(@_) - $_[-1] == 1) && isIncreasingArray(@_);
}
Run Code Online (Sandbox Code Playgroud)
至于子程序参数是一个数组与多个参数,请按照这种方式考虑:Perl总是向子程序发送一个参数列表作为数组@_.您可以将该数组中的参数作为单个标量移位或弹出,或者以整数列表作为数组运行.从你的子程序里面,它仍然是一个数组,句号.
如果你进入引用,是的,你可以将引用数组传递给子程序.该引用在技术上仍然作为包含一个标量值的数组(列表)传递给子例程:引用.首先,我会忽略所有这些,并在没有引用的情况下围绕基本操作.
调用子程序.这样,Perl就会秘密地将你的scalars列表转换为一系列标量:
isIncreasingArray(1,2,3,4);
Run Code Online (Sandbox Code Playgroud)
这样,Perl传递你的数组:
@a = (1,2,3,4);
$answer = isIncreasingArray(@a);
Run Code Online (Sandbox Code Playgroud)
无论哪种方式,子程序都会得到一个数组.它是一个副本*,因此在这里引用效率.不要担心,对于K <10,000,即使这里我的效率低,学术,优雅,递归的解决方案,我的笔记本电脑仍然需要不到1秒钟:
print isIncreasingArray(1..10000), "\n"; # true
Run Code Online (Sandbox Code Playgroud)
*副本:有点但不是真的吗?请参阅下面的评论和其他资源,例如PerlMonks."有人可能会说Perl总是通过引用,但保护我们自己." 有时.实际上,我在子程序中创建自己的副本到本地化的"我的"变量.就这样做.
归档时间: |
|
查看次数: |
2647 次 |
最近记录: |