在 Perl 中,如何测试序列是否为 n, n + 形式? 1、n+ 2, ..., n + k?
我正在尝试实现一个子例程,它接受一个数组作为其参数(或使用多个参数 - 仍然没有完全理解差异),并根据该数组是否是一个返回 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
这就是我的想法:
sub isIncreasingArray {
my $last;
foreach $n (@_) {
return 0 if defined($last) && $last != $n - 1;
$last = int($n);
}
return 1;
}
我对 Perl 很陌生,想知道是否有更简单或更简洁的方法来实现这一点?另外,我写的内容符合最佳实践吗?
I'm trying to implement a subroutine that takes an array as its argument (or uses multiple arguments — still haven't quite grokked the difference), and returns true or false depending on whether that array is an increasing sequence (each number must be 1 more than the last):
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
This is what I've come up with:
sub isIncreasingArray {
my $last;
foreach $n (@_) {
return 0 if defined($last) && $last != $n - 1;
$last = int($n);
}
return 1;
}
I'm quite new to Perl and am wondering if there's a simpler or more concise way of achieving this? Also, is what I've written in line with best practices?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
这是我能想到的最短形式,检查映射中的每个元素,看看它是否等于增加的 self,返回一组 0 和 1,计算 1 并与该集合的原始大小进行匹配。
This is the shortest form I could come up to, check each element in a map to see if it is equal to the increased self, return a set of 0 and 1, count the 1 and match against the original size of the set.
无论您使用哪种实现,事先进行一些快速检查都不会有什么坏处:
Whatever implementation you use, it wouldn't hurt to make some quick checks beforehand:
为什么没有人想出智能匹配的解决方案?
虽然这个解决方案不如其他一些解决方案高效,但它还具有使用字符串的额外好处。
编辑
Sub 现在对于空列表和单元素列表返回 true,因为这就是专家所说的应该做的:
我喜欢我的潜艇是单线的!
How come no one's come up with a smart-match solution?
While this one isn't as efficient as some of the other solutions, it has the added benefit of working with strings as well.
EDIT
Sub now returns true for empty and single-element lists because that's what the experts say it should do:
I love it when my sub's a single-line!
我最终得到的东西比你的长一点。我认为这意味着您的解决方案没有任何问题:)
I ended up with something a little longer than yours. Which means, I suppose, that there's nothing wrong with your solution :)
使用 6 之前的“连接点”:
none
表达式也可以写成(更神秘)为(如果约束是 $x[$n+1] - $x[$n] = = 1,然后减去 1 也得到了“Perl 真值条件”。)
实际上,认为“无”连接运算符有点倒退了这个概念,所以我将使用
all
:Using a pre-6 "junction":
The
none
expression could also be written (more cryptically) as(If the constraint is that $x[$n+1] - $x[$n] == 1, then subtracting 1 makes a "Perl truth condition" as well.)
Actually come to think of it a 'none' junction operator is kind of backward to the concept, so I'll use the
all
:有人必须在这里引入函数式编程解决方案,因为这种数学公式只需要递归。 ;)
至于子例程参数是数组还是多个参数,请这样想:Perl 总是将参数列表作为数组 @_ 发送到子例程。您可以从该数组中移动或弹出参数作为单独的标量,或者以其他方式将整个列表作为数组进行操作。从你的子例程内部来看,它仍然是一个数组,句号。
如果您涉及引用,是的,您可以将对数组的引用传递到子例程中。从技术上讲,该引用仍然作为包含一个标量值的数组(列表)传递给您的子例程:引用。首先,我会忽略所有这些,让您在没有参考资料的情况下专注于基本操作。
调用子程序。这样,Perl 就会秘密地将您的标量列表转换为标量数组:
这样,Perl 就会传递您的数组:
无论哪种方式,子例程都会获得一个数组。这是一个副本*,因此这里讨论参考文献的效率。不要担心 K<10,000,即使我这里的解决方案效率低得可笑,学术性的,优雅的,递归的,在我的笔记本电脑上仍然需要不到 1 秒:
*副本:有点,但不是真的?请参阅下面的评论和其他资源,例如 PerlMonks。 “有人可能会说 Perl 总是进行引用传递,但可以保护我们免受我们自己的伤害。”有时。在实践中,我在子例程内将自己的副本复制到本地化的“我的”变量中。就这么做吧。
Someone has to throw in the functional-programming solution here, since this sort of mathematical formula just begs for recursion. ;)
As for a subroutine argument being an array versus multiple arguments, think of it this way: Perl is always sending a list of arguments to your subroutine as the array @_. You can either shift or pop off arguments from that array as individual scalars, or otherwise operate on the whole list as an array. From inside your subroutine, it's still an array, period.
If you get into references, yes you can pass a reference-to-an-array into a subroutine. That reference is still technically being passed to your subroutine as an array (list) containing one scalar value: the reference. First I'd ignore all this and wrap your head around basic operation without references.
Calling the subroutine. This way, Perl is secretly converting your bare list of scalars into an array of scalars:
This way, Perl is passing your array:
Either way, the subroutine gets an array. And it's a copy*, hence the efficiency talk of references here. Don't worry about that for K<10,000, even with my ridiculously inefficient, academic, elegant, recursive solution here, which still takes under 1 second on my laptop:
*A copy: sort of, but not really? See comments below, and other resources, e.g. PerlMonks. "One might argue that Perl always does Pass-By-Reference, but protects us from ourselves." Sometimes. In practice I make my own copies inside subroutines into localized "my" variables. Just do that.
有几点:
为了提高效率,特别是为了最大限度地减少内存占用,您可能希望将对数组的引用传递给子例程。
在列表上下文中,
return 0
将返回由单个元素组成的列表,因此为 true。当您想要返回false
并在所有上下文中执行此操作时,只需return
就足够了。通过比较第一个和最后一个、第二个和倒数第二个等之间的差异,可以将比较次数减少一半,以查看差异等于索引差异,但我不认为这样 现在就很清楚了。
这是与您的版本略有不同的版本。请注意,您应该使用
strict
并确保使用my
确定循环变量的范围:当然,还有一些基准测试:
A couple of points:
For efficiency, especially to minimize memory footprint, you probably want to pass a reference to an array to the subroutine.
In list context,
return 0
will return a list consisting of a single element and thus will be true. a barereturn
suffices when you want to returnfalse
and does the job in all contexts.It is probably possible to cut the number of comparisons in half by comparing the difference between the first and the last, the second and the second last etc. to see differences equal difference in indexes, but I am not thinking that clearly right now.
Here is a slightly different version based on yours. Note that you should use
strict
and make sure to scope your loop variable usingmy
:And, of course, some benchmarks: