【问题标题】:“P6opaque, Str” vs simple “Str” types in Perl 6Perl 6 中的“P6opaque, Str”与简单的“Str”类型
【发布时间】:2019-07-21 18:50:49
【问题描述】:

这是my previous question的后续行动。

我终于可以在这里重现错误了:

my @recentList = prompt("Get recentList: e.g. 1 2 3: ").words || (2,4,6);    
say "the list is: ", @recentList;    
for @recentList -> $x {
    say "one element is:  ", $x;
    say "element type is: ", $x.WHAT;
    say "test (1,2,3).tail(\"2\") : ", (1,2,3).tail("2");
    say ( (10.rand.Int xx 10) xx 15 ).map: { @($_.tail($x)); };
}

只要我使用默认列表,只需在提示符处按回车键而不输入任何内容,结果就可以了。但是如果我输入一个数字,它会给出这个错误:

Get recentList: e.g. 1 2 3: 2
the list is: [2]
one element is:  2
element type is: (Str)
test (1,2,3).tail("2") : (2 3)
This type cannot unbox to a native integer: P6opaque, Str
  in block  at intType.p6 line 9
  in block <unit> at intType.p6 line 5

如果 tail("2") 有效,为什么 tail($x) 会失败?另外,在我的原始代码中,tail($x.Int) 不会纠正问题,但在这里可以解决。

【问题讨论】:

  • 酷。 :) 第 1 步是让代码显示问题。第 2 步是打高尔夫球。到目前为止,我有(1 xx 1).tail('1')。 :)
  • 好的,回到这个问题。 say Any.tail('0') 显示 ()say Any.tail('1') 产生 This type cannot unbox to a native integer: P6opaque, Str。这已经足够高尔夫了。接下来,一些代码探索......

标签: string int tail raku


【解决方案1】:

这充其量只是一个答案。迄今为止,解决这个问题的尝试失败了。我可能只是在杂草丛中徘徊。但我会公布我所拥有的。如果不出意外,也许它可以提醒您以下前三个步骤是明智的;此后,我通过深入研究源代码来赌我前进的能力,而我可能会通过直接调试第三步中讨论的编译器来获得更快、更可靠的进展。


好的,第一步是MRE。你提供的是一个完全是 R 和足够 M 的 E。:)


第 2 步是增加 M(高尔夫)。我把它归结为:

Any.tail('0');    # OK
Any.tail('1');    # BOOM

注意,它可以是实际值:

1.tail('1');      # BOOM
(1..2).tail('1'); # BOOM

但有些值是有效的:

(1,2).tail('1');  # OK

第 3 步可能应该按照Playing with the code of Rakudo Perl 6 中的说明跟踪编译器的执行,例如在其源代码中粘贴says 并重新编译它。

您可能还想试试App::MoarVM::Debug。 (我没有。)

使用这些方法,您将能够以绝对精确的方式跟踪编译器对您抛出的任何代码所做的操作。我建议你这样做,即使我没有这样做。也许你能找出我哪里出错了。


在下文中,我通过直接探索 Rakudo 编译器的源代码来跟踪这个问题。

search for "method tail" in the Rakudo sources 产生了 4 个匹配项。对于我的高尔夫,匹配方法是a match in core/AnyIterableMethods.pm6

tail 参数$n 显然不是Callable,所以继续我们探索的相关行是Rakudo::Iterator.LastNValues(self.iterator,$n,'tail')

core/Iterator.pm6 中搜索这个会导致this method

这反过来又调用this .new routine

These three lines:

nqp::if(
  n <= 0,                 # must be HLL comparison
  Rakudo::Iterator.Empty, # negative is just nothing

解释为什么'0' 有效。 &lt;= 运算符在进行数值比较之前将其操作数强制为数值。所以'0' 强制转换为0,条件为True,结果为Rakudo::Iterator.EmptyAny.tail('0') 产生() 并且不会抱怨。

紧跟以上三行的代码是nqp::ifelse 分支。它以nqp::create(self)!SET-SELF(iterator,n,f) 结束。

然后调用!SET-SELF 例程,该例程具有以下行:

($!lastn := nqp::setelems(nqp::list, $!size = size)),

尝试将size(在我们的BOOM 案例中为'1')分配给$!size。但是$!size is declared as

has int $!size;

宾果游戏。


是吗?我不知道我是否真的 正确地追踪到了问题所在。我只是在 github 存储库中探索代码,而不是实际运行编译器的检测版本并跟踪其执行,正如在尝试找出您遇到的问题的明智步骤 3 中所讨论的那样。

更糟糕的是,当我运行编译器时,它是旧的,而我正在探索的代码是 master...


为什么会这样?

(*,*).tail('1') # OK

这个代码路径大概是this method。参数$n 不是Callable,因此代码路径将通过在行中使用$n 的路径运行:

              nqp::unless(
                nqp::istype($n,Whatever) || $n == Inf,
                $iterator.skip-at-least(nqp::elems($!reified) - $n.Int)

$n == Inf 应该不是问题。 == 会将其操作数强制转换为数字,这应该注意 $n'1'

nqp::elems($!reified) - $n.Int 也不应该是个问题。

nqp ops 文档显示nqp::elems always returns an int。所以这归结为一个应该可以工作的int - Int

嗯。

A blame of these lines 表示the .Int in the last line was only added 3 months ago

所以,抓着稻草,如果一个人尝试会发生什么:

(my int $foo = 1) - '1' # OK

不,这不是问题。

看来线索已经变冷了,或者更确切地说,我已经偏离了实际的执行路径。

我会发布我所拥有的。也许其他人可以从这里取走,或者我会在一三天内再去一次......

【讨论】:

  • 非常感谢 raiph !!!你的解释总是那么详细。我看到答案比我想象的要深,我将尝试深入研究 App::MoarVM::Debug 和 Rakudo 源代码。这本身就是一场巨大的冒险。再次感谢雷夫!!!
猜你喜欢
  • 1970-01-01
  • 2013-08-04
  • 1970-01-01
  • 2022-07-03
  • 2021-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多