配列の添字のラップアラウンド

ふと配列のインデックスをラップアラウンドする必要があって、考えてみたらこんなのにしばらく悩んでけっこう凹んだ。
元のはVimスクリプトだったんだけどPerlで。

#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;


sub array_get_elem {
    my ($array, $idx) = @_;
    return $array->[$idx] if exists $array->[$idx];

    if ($idx < 0) {
        # fortunately recent LL languages support negative index :)
        $idx = -(abs($idx) % @$array);
        # But if you want to access to 'real' index:
        # if ($idx != 0) {
        #     $idx = @$array + $idx
        # }
    }
    else {
        $idx = $idx % @$array;
    }
    return $array->[$idx];
}

is(array_get_elem([qw(a b c d e)], 1), 'b', "qw(a b c d e)[1] is b");
is(array_get_elem([qw(a b c d e)], 5), 'a', "qw(a b c d e)[5] is a");
is(array_get_elem([qw(a b c d e)], -6), 'e', "qw(a b c d e)[-6] is e");
ok 1 - qw(a b c d e)[1] is b
ok 2 - qw(a b c d e)[5] is a
ok 3 - qw(a b c d e)[-6] is e
1..3

これが関数じゃなくて配列で不正な添字に対して警告が出なければどんどんエンバグできて便利ですね。というわけで作ってみ...ようかと思ってAcme::Tie::KYArray*1っての作ろうとしたけどできなかった。
というのも配列のtieの仕組みが添字を受け取った時点でFETCHSIZEを呼び出してサイズを確認し、無効なインデックスだったらエラーが出るようになってるから...
XSで書けばできるんだろうか。
とりあえず眠いので今日はここまで。

*1:空気読んでる