那是Raku哈希还是块?

14
这是一个可能会困扰初学者的意外行为。首先,这是有意为之吗?其次,Raku使用哪些其他元素来猜测要创建哪个对象?它开始时认为它是块或哈希,然后再改变,还是在最后决定?
你可以使用大括号和fat箭头构建哈希
my $color-name-to-rgb = {
    'red' => 'FF0000',
    };

put $color-name-to-rgb.^name;  # Hash

使用另一种Pair符号也会创建一个哈希
my $color-name-to-rgb = {
    :red('FF0000'),
    };

但是,如果没有胖箭头,我会得到一个

my $color-name-to-rgb = {
    'red', 'FF0000',
    };

put $color-name-to-rgb.^name;  # Block

Hash文档仅提到在大括号内使用$_会创建一个Block

还有其他定义哈希的方法,但我只是询问这个特定的语法,而不是寻找我已经知道的解决方法。

$ perl6 -v
This is Rakudo version 2017.04.3 built on MoarVM version 2017.04-53-g66c6dda
implementing Perl 6.c.
2个回答

16

您的问题1和此答案仅适用于括号代码({...})在术语位置2

规则

如果括号代码没有签名或包含顶层语句,并且为空或仅包含列表,其第一个元素是带有%标记的变量(例如%foo)或一个文字对(例如:bar),那么它是一个Hash

say WHAT {                  }             # (Hash)
say WHAT { %foo             }             # (Hash)
say WHAT { %foo, ...        }             # (Hash)
say WHAT { foo => 42, ...   }             # (Hash)
say WHAT { :foo, ...        }             # (Hash)
say WHAT { key => $foo, ... }             # (Hash)

否则它是一个Block

为了强制使用HashBlock解析

  • 要编写一个空的Hash术语,请写{}

  • 要编写一个空的Block术语,请写{;}

  • 要强制将{...}术语构建为Block而不是Hash,请包含一个;,例如{; ... }

  • 要强制将{...}术语构建为Hash而不是Block,请按照上面的摘要/下面的详细规则(或者在花括号代码中写入%(...))。

详细示例

如果有人认为上述相对简单的规则有误,本回答的其余部分提供了详尽的细节。


一些带大括号的代码具有 显式 签名,即它具有显式参数,例如下面的 $foo。无论大括号内有什么内容,它总是构造一个 Block

say WHAT         { key => $foo, 'a', 'b' } # (Hash)
say WHAT -> $foo { key => $foo, 'a', 'b' } # (Block)

一些带有括号的代码由于括号内遇到的语法而生成了一个隐式签名:
- 在括号内使用“代词”(`$_`,`@_`或`%_`)。这将导致一个带有签名的`Block`(如果没有显式签名,则为隐式签名)。使用`@_`或`%_`代词始终是显式的;使用`$_`可以是显式的,也可以是隐含的,例如在没有左侧参数的方法操作符(如`.method`)中。换句话说,即使`{ :foo, .key, .value }`也是一个带有签名的`Block`(`(;; $_? is raw)`),因为`.key`缺少左侧参数。
- 使用“占位符”变量(例如`$^foo`)。
与显式签名一样,如果带有隐式签名的括号代码,则无论括号内有什么内容,它始终构造一个`Block`。
say WHAT { key => @_ }                     # (Block)
say WHAT { key => 'value', .foo, .bar }    # (Block)

顶层语句意味着它是一个

say WHAT { :foo; (do 'a'), (do 'b') }     # (Block)
say WHAT { :foo, (do 'a'), (do 'b') }     # (Hash)

第一行中的括号代码以:foo;开头;;表示括号代码包含语句。
第二行包含多个语句,但括号代码不将它们视为顶级语句。相反,它们在一个列表的各个元素中产生值,而列表本身不是一个语句。
对于标识符的顶级声明也意味着它是一个Block。声明是一种语句,但我包含了这一部分,以防有人没有意识到这一点。
say WHAT { :foo, $baz, {my $bar} }        # (Hash)
say WHAT { :foo, $baz, (my $bar) }        # (Block)

第一行包含一个作为值的Block,其中包含一个声明(my $bar)。但是该声明属于内部的{my $bar} Block,而不是外部的{...}。因此,就外部{...}来说,内部的Block只是一个值,因此该外部括号代码仍然被解释为一个Hash
相比之下,第二行在{...}内部的括号中声明了一个变量。因此,括号代码是一个Block

正如开头所指出的,要成为一个Hash,大括号代码的内容必须是以%标记的变量或者文字对的列表。因此,以下所有内容都将产生Block

my $bar = key => 'value';
say WHAT { $bar, %baz }                   # (Block)
say WHAT { |%baz      }                   # (Block)
say WHAT { %@quux     }                   # (Block)
say WHAT { 'a', 'b', key => $foo }        # (Block)
say WHAT { Pair.new: 'key', $foo }        # (Block)

脚注

1 这个“Hash还是Block?”的问题是DWIM设计的一个例子。在Raku文化中,良好的DWIM设计被认为是一件好事。但是每个DWIM都伴随着相应的WATs3。良好的DWIM设计的关键是确保总体上WATs的吠声比咬人严重4;而且这些吠声是有用的5;并且考虑到DWIM的净效益远远超过了所有的吠声和咬人行为6

2 在Raku中,术语类似于英语中的名词或名词短语。它是一个值。

以下是术语的示例:

.say given { ... }  # closure? hash?
say 42, { ... }     # closure? hash?

示例的带括号代码不是术语。
if True { ... }     # always a closure
class foo { ... }   # always a package
put bar{ ... }      # always a hash index

这个回答只讨论作为术语的大括号代码。关于术语的更多细节,或者更具体地说是“术语位置”(在语法中将大括号代码解释为术语的位置),请参见本回答下面的评论。

3 WAT 是指开发人员对某些事情感到惊讶和不可思议。众所周知,即使对于设计良好的DWIM(Do What I Mean)功能,对于大多数人来说,大部分时间都能正常工作,但总会有一些相关的WATs会让一些人感到惊讶,其中甚至包括那些在其他时间从DWIM中受益的人。

4 与此DWIM相关的WATs的影响程度各不相同。通常会有一个明显的错误信息(狗叫声),以便解决问题。但它也可能更加隐晦:

say { a => 42 }() ;  # No such method 'CALL-ME' for invocant of type 'Hash'   WAT? Oh.
say { a => $_ }<a> ; # Type Block does not support associative indexing.      WAT? Oh.

say { a => $_, b => 42, c => 99 } .elems  # 1                                 WAT?????

5“ bark ”是文档中的错误消息或警告。这些通常可以改进。参见Lock.protect({}) fails, but with surprising message

6社区成员对于DWIM设计是否值得,或者任何特定的DWIM是否值得,意见不一。参见my perspective和Sam对这个问题的回答。


2
“in term position” 是什么意思?我们是否有“positions”的概念?如果是这样,它们应该被记录下来。 - jjmerelo
1
术语位置指术语在语言语法中可接受的位置。正如我链接的文档所说,“Perl 6 中的大多数语法结构都可以按术语和运算符进行分类。” 我认为它对应于一个值,其中包括一个表达式,该表达式无法进一步分解为值列表。在英语方面,就像名词或名词短语可以出现的位置一样。在实现方面,我认为它对应于Rakudo的Perl 6 Grammar.nqp文件中的“term”规则... - raiph
请看这里和这里,它们分别是被聚集在一起的tokens(https://github.com/rakudo/rakudo/blob/1f89b12ee871c6bd5d753acb916ec495d959dc46/src/Perl6/Grammar.nqp#L1466-L1557和https://github.com/rakudo/rakudo/blob/1f89b12ee871c6bd5d753acb916ec495d959dc46/src/Perl6/Grammar.nqp#L3000-L3177)。此外还有[`regex term:sym<reduce>](https://github.com/rakudo/rakudo/blob/1f89b12ee871c6bd5d753acb916ec495d959dc46/src/Perl6/Grammar.nqp#L3788-L3813)以及可能的[token termish`](https://github.com/rakudo/rakudo/blob/1f89b12ee871c6bd5d753acb916ec495d959dc46/src/Perl6/Grammar.nqp#L3627-L3659)。 - raiph
2
我在设计文档中找到的“term position”匹配项都没有定义它。我认为这是一个众所周知的术语,在解析一般和P5文化特别方面,因此Larry没有费心去定义它。这次尝试追踪它让我想起了我以前研究过它...这让我想到了我的SO答案中的“什么是术语?”部分。这很无聊。我建议问问Larry。 - raiph
1
目前我得出的结论是,“term position”归结为在Grammar.nqp中调用<term>的位置。我看到有四个这样的调用,其中主要的两个出现在termish标记中此处。这就是我的回答“你所说的‘在术语位置上’是什么意思?”至于“我们是否有‘位置’这样的东西?”,我认为这些是语法中的句法槽。我想这就是我能为你提供的全部信息了。希望对你有所帮助。@jjmerelo ^^ - raiph
2
我现在为此创建了一个问题 https://github.com/Raku/doc/issues/3774应该早就这样做了... - jjmerelo

14

在 Perl6 中,首选的方法是使用 %( ) 来创建哈希表。

my $color-name-to-rgb = %(
    'red', 'FF0000',
    );

我不建议人们使用花括号创建哈希表。如果他们想要创建哈希表,那么%( )是正确的方法。

如果你来自Perl 5的世界,最好养成使用%( )而不是{ }来创建哈希表的习惯。


2
我记得以前用 '{ }' 创建哈希会抛出错误并告诉你要使用 '%( )'。在我看来,这种行为应该恢复。 - ab5tract
7
那么,文档需要进行重大更新以反映这一点。当“{}”无法使用时,提到了“%()”作为替代方法。如果它不是在文档中,那么有关首选方法的来源在哪里? - brian d foy
2
我完全同意你的评估,Brian。我在这里打开了一个文档问题 https://github.com/perl6/doc/issues/1380我们曾经有过弃用警告,会在程序完成后显示,也许当您使用{}创建哈希时,这是我们想要添加到6.d中的内容。 - Samantha M.
现在已经将此作为建议包含在样式指南中。感谢您提出的问题。 - jjmerelo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接