我能不能有一个ArrayOutofBoundsException



在 Python 中,如果你使用越界键/索引索引集合结构,你会得到一记耳光:

>>> [1, 2, 3][9]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

这是一个例外;它派生自BaseException,如果不能处理一个,我的程序就会崩溃,这几乎总是我想要的


Perl 5 和 6 的列表索引似乎并不关心越界索引:

$ perl6
> my @l = (1..4);
[1 2 3 4]
> say @l[2];
3
> say @l[9];
(Any)
> print @l[9];                                                                                                          
Use of uninitialized value @l of type Any in string context
<snip>    
True
> my $x = @l[9]; # even assignment doesn't error! who decided this was okay!?
> say $x; print $x;
(Any)
Use of uninitialized value $x of type Any in string context
<snip>    

在 Perl 5 中基本上是相同的,只是你没有得到返回的值,但执行继续正常。

我不明白为什么越界访问应该是沉默的。您得到该值可能"未初始化"的唯一警告(但我们都知道它实际上意味着不存在)是当您将其提供给某些函数时。

我可以以某种方式解决这个问题吗?我可以实现我自己的 circumfix 后索引运算符来覆盖在垃圾索引上死亡的默认索引运算符,但是无法区分未初始化的值和类型 Any.可以看到的唯一方法是检查请求的索引是否在List.elems()范围内。

我可以使用什么(最好是最小、简单、干净、可读等)解决方案来解决此问题?


在任何人说"是的,但变量是未初始化的,就像my $x;!"之前:在 C 中,如果你访问你没有分配的内存,你会得到一个段错误;为什么我不能有这种安全性?


我将其标记为 Perl 和 Perl 6,因为当我学习 Perl 6 并且这个问题的细节主要适用于 6 时,主要思想似乎是 5 和 6 的共同方面。

Perl 6 有整形数组来强制数组边界在简单或多维数组上。

从S09开始:

my int @ints[4;2];          # Valid indices are 0..3 ; 0..1
my @calendar[12;31;24];     # Valid indices are 0..11 ; 0..30 ; 0..23

更多示例:

use v6;
# declare an array with four elements
my @l[4] = (1..4);
try { @l.push: 42}
say @l;
# [1 2 3 4]

这些可以是多维的

my @tic_tac_toe[3;3] = <x o x>, <o . o>, < x o x>;
@tic_tac_toe[1;1] = "x";      # ok
try @tic_tac_toe[3][3] = "o"; # illegal
say @tic_tac_toe;
# [[x o x] [o x o] [x o x]]

这将做你想要的。

my @a is default(Failure.new('IndexError'));
@a[0] = 1;
say @a[1];
say 'alive';
output:
===SORRY!===
IndexError

如果你想要一个堆栈跟踪,你必须运行perl6 --ll-exception或使用Proxy创建自己的容器。(这是一个错误,报告为:RT#127414)

see: https://doc.perl6.org/type/Failure
see: https://doc.perl6.org/routine/is%20default
see: https://perl6advent.wordpress.com/2015/12/02/day-2-2-bind-or-2-bind/

哦,请不要听那些想告诉你要适应 Perl 6 的反对者的话。Perl 6 的全部意义在于你可以修补它,并按照你的意愿弯曲它。

class NonvivArray is Array {
    multi method AT-POS(NonvivArray:D: Int:D $pos) is raw {
        say 'foo';
        my $val = callsame;
        X::OutOfRange.new(got=>$pos, range=>0..self.elems-1).throw unless $val;
        $val;
    }
    multi method AT-POS(NonvivArray:D: int $ipos) is raw {
        say 'foo';
        my $val = callsame;
        X::OutOfRange.new(got=>$ipos, range=>0..self.elems-1).throw unless $val;
        $val;
    }
}
my NonvivArray $a;
$a.push: 1;
dd $a;
say $a[1];

有两种方法可以做到这一点。这是第三个。

try { 
    my Int:D @a; 
    say @a[0]; 
    CATCH { default { say .^name } } 
}
# OUTPUT«X::Method::NotFound␤»

有人可能会争辩说,抛出的异常是LTA,或者这实际上应该是特例。但是,它完成了OP的要求,而无需摆弄乐道实施的Array的实施细节。

[既然你同时要求 Perl5 和 Perl6 的答案,但你只得到了 Perl6 的答案,这里有一个 Perl5 的答案。

您可以编写一个操作检查器,将索引操作替换为检查边界的操作(就像no autovivification;将取消引用的操作替换为不自动生存的版本一样。

你可以从 Acme::Array::MaxSize 中获得灵感 - 即你可以使用 Tie::Array 来拦截数组操作。

最新更新