这只是一个至多的答案,是迄今为止尝试解决这个问题失败的尝试。我可能已经走进了泥淖。但我会公布我所拥有的东西。如果说没有其他作用,也许它可以作为一个提醒:以下的前三步是明智的;此后,我在赌我的能力,通过探索源代码来推进,当我可能通过直接调试编译器来讨论第三步而取得更快更可靠的进展。
好的,第一步是进行MRE。你提供的是一个完全R且足够M的E。 :)
第二步是增加M(高尔夫)值。我将其降至:
Any.tail('0')
Any.tail('1')
请注意,它可以是实际值:
1.tail('1')
(1..2).tail('1')
但是一些值是有效的:
(1,2).tail('1')
第三步应该是按照Playing with the code of Rakudo Perl 6的说明来跟踪编译器的执行,例如在其源代码中添加say
并重新编译。
您还可以尝试使用App::MoarVM::Debug。(我没有尝试过。)
使用这些方法,您将拥有绝对精确地跟踪编译器处理任何代码的能力。我建议您这样做,即使我没有这样做。也许您可以找出我的错误所在。
以下我将通过直接深入挖掘Rakudo编译器的源代码来跟踪这个问题。
在Rakudo源代码中
搜索"method tail",找到了4个匹配项。对于我的高尔夫比赛,匹配方法是
"core/AnyIterableMethods.pm6"中的一个匹配项。
参数
$n
显然不是一个
Callable
,因此继续我们的探究的相关行是
Rakudo::Iterator.LastNValues(self.iterator,$n,'tail')
。
搜索结果指向 core/Iterator.pm6
中的 该方法。
该方法又调用了 .new
例程。
以下三行代码:
nqp::if(
n <= 0, # must be HLL comparison
Rakudo::Iterator.Empty, # negative is just nothing
解释为什么
'0'
起作用。运算符
<=
在执行数字比较之前会将其操作数强制转换为数字。因此,
'0'
被强制转换为
0
,条件为
True
,结果为
Rakudo::Iterator.Empty
,
Any.tail('0')
产生
()
并且不报错。
紧接着上述三行代码的是
nqp::if
的
else分支。它以
nqp::create(self)!SET-SELF(iterator,n,f)
结尾。
这反过来调用了
!SET-SELF
例程,其中包含以下行:
($!lastn := nqp::setelems(nqp::list, $!size = size)),
这里试图将size
赋值给$!size
,在我们的BOOM案例中,size
的值为'1'
。但是$!size
被声明为:
has int $!size;
或者不是这样吗?我不确定我是否已经正确地追踪到了问题所在。我只是在github仓库中浏览代码,而没有实际运行编译器的受控版本并跟踪其执行,正如我们讨论的尝试解决你遇到的问题的明智步骤#3。
更糟糕的是,当我运行编译器时,它是旧版的,而我正在浏览的代码是master
...
为什么这个有效?
(*,*).tail('1')
这将要使用的代码路径可能是
此方法。参数
$n
不是
Callable
,因此代码路径将通过在以下行中使用
$n
的路径运行:
nqp::unless(
nqp::istype($n,Whatever) || $n == Inf,
$iterator.skip-at-least(nqp::elems($!reified) - $n.Int)
< p >
$n == 无穷大
不应该是一个问题。
==
将强制其操作数为数字,这应该可以处理
$n
是
'1'
的情况。
nqp::elems($!reified) - $n.Int
也不应该是一个问题。
nqp ops 文档显示 nqp::elems
总是返回一个 int
。 因此,这归结为 int - Int
,应该可以工作。
嗯。
这些代码行的错误表明最后一行中的.Int
仅于3个月前添加。
因此,试图抓住救命稻草,如果尝试:
(my int $foo = 1) - '1' # OK
不,那不是问题所在。
看起来线索已经断了,或者说我已经偏离了实际的执行路径。
我会发布我所拥有的。也许其他人可以从这里接手,或者我会在一两天后再试一次...
(1 xx 1).tail('1')
。 :) - raiphsay Any.tail('0')
显示()
,但是say Any.tail('1')
产生了This type cannot unbox to a native integer: P6opaque, Str
。我想这已经足够了。接下来,一些代码挖掘... - raiph