Perl6/Raku:如何限制变量允许的值?

7

Perl6/Raku

我想创建一个子程序,只允许传递特定的值。不传递允许的值将会创建一个检查错误(perl6 -c)。

我该如何实现这个功能?

非常感谢, -T

嗨Raiph,

在我的WinPopUps模块中,我使用了“where”方法,因为它可以一眼告诉用户允许的值是什么。我喜欢它!这一切都与可维护性有关!(顺便说一句,以下是你创建的怪物!)

sub WinPopUp( Str $TitleStr, 
              Str $MessageStr,
              Str $Icons where   * ~~ "Exclamation"             |
                                      "Warning"                 |
                                      "Information"             |
                                      "Asterisk"                |
                                      "Question"                |
                                      "Stop"                    |
                                      "Error"                   |
                                      "Hand",
              Str $Buttons where * ~~ "AbortRetryIgnore"        | 
                                      "CancelTryAgainContinue"  |
                                      "Help"                    |
                                      "Ok"                      |
                                      "OkCancel"                |
                                      "RetryCancel"             |
                                      "YesNo"                   |
                                      "YesNoCancel" ) 
              is export( :WinPopUp ) {

感谢您的帮助! -T
如果您需要整个模块以及发布位置,请告诉我。

你也可以将值存储在数组常量中,然后作为 sub WinPopUp( … Str $Icons where * eq @icons.any, Str $Buttons where * eq @buttons…) 的引用。你还可以考虑使用 Str() 而不是 Str,这样可以传递可以转换为字符串的非字符串。 - user0721090601
这是一个模块。主程序只能看到被调用子程序内部的内容。 - Todd
顺便说一句,我收集了一些与Windows API相关的函数和常量以及NativeCall定义。我可以分享给你。这些都来自于user32和kernel32。 - Holli
很想看到它们!对我来说,本地调用是一个谜。 - Todd
Todd:主程序只需要看到子程序。但是,在模块中声明的任何变量,即使其他导入该模块的程序无法使用它们,仍然可以在模块内部使用。因此,您只需在子程序声明上面放置我的 @icons = <foo bar>; 即可。 - user0721090601
从我对被接受的答案的评论中复制... perl6 -c 只进行编译时检查。这不包括 where 子句。它们只在运行时检查。 - raiph
2个回答

12
您可以简单地在值上添加一个where条件。
sub foo( Int $binary where * ~~ 0|1 ) { ... }
where条件可以是任意代码块(甚至是子代码块)。
如果您需要多次使用该条件,可以创建一个subset
subset BinaryInt of Int where * ~~ 0|1;

然后在签名中使用它

sub foo( BinaryInt $binary ) { ... }

请注意,这不仅限于子程序签名。这些约束/条件在任何地方都会被执行。
my BinaryInt $i = 0; 
$i++; 
$i++;
# -> Type check failed in assignment to $i; expected BinaryInt but got Int (2)

您还可以有子集的子集:

subset FalseBinaryInt of BinaryInt where not *;
my FalseBinaryInt $i = 0; 
$i++; 
# -> Type check failed in assignment to $i; expected FalseBinaryInt but got Int (1)

编辑:下面的JJ是正确的。在这种情况下,使用枚举很有用。

sub WinPopUp( Str $TitleStr, 
              Str $MessageStr,
              MessageBoxIcons $Icons where   * ~~ Exclamation |
                                                  Information |
              ...

配合像枚举这样的东西使用
enum MessageBoxIcons is export {
    Exclamation => 0x00000030,
    Information => 0x00000040,
    ...
}

枚举成员是符号,如果你拼错了一个,编译器会捕捉到它,从而保护您免受随意的拼写错误。此外,您无需查找要输入 MessageBoxW 的值(我想这就是你正在做的事情)。

说到 MessageBoxW,我建议将您的子例程命名为 message-box(在 Raku 中,我们倾向于仅为类和类型等使用 CamelCase,以保持一致性)。这样也能与 MessageBoxW 保持一致。


1
这是很酷的东西。@Todd已经接受了你出色的答案(并且得到了我的赞和许多其他人的赞)。但是,FTR perl6 -c 只进行编译时检查。这不包括 where 子句。它们只在运行时检查。 - raiph
嗨JJ,我一直使用CamelCase,因为我可以打字而不会有任何影响。而且CamelCase告诉我什么是我的工作,什么是Raku的工作。这让区分它们非常容易。对我来说,这使得代码更易于维护。为了实现这一点,Raku需要保持全小写。 - Todd

0
在你的情况下,最好使用 枚举
enum Icons <Exclamation Warning Information>;
sub pop-up( Icons $icon ) { $icon}; 
say pop-up( Information ); # OUTPUT: «Information␤»

然而,你已经出于某种原因在使用Str,所以最好使用subsets
subset Icons of Str where * eq any <Exclamation Warning Information>;
sub pop-up( Icons $icon ) { $icon};
say pop-up( "Information" ); 

这些已经在Holli's answer中提到过,尽管只有“如果您将多次使用它们”。即使只使用一次,我也会定义一个子集。这样更安全、更清晰,也更易于测试。


嗨JJ,这是一个模块。据我所知,主程序只能看到被调用模块内部的内容。虽然我已经好几年没有测试过了,但这也是我的经验。我选择“where”,因为我可以以一种直观的方式编写子标题,让读者知道哪些值被接受到子程序中。因此,为了可维护性。谢谢你的帮助! - Todd
嗨,霍莉,我不确定你的编辑在哪里。Stack Exchange并不是真正友好于信息的流动,因为他们希望你编辑原始内容,而评论部分有时候无法提供足够的空间进行适当的回应。你指的是什么?此外,我不知道调用程序是否可以看到除了它所调用的子程序之外的模块中的任何内容。我的经验是它不能,但这可能已经改变了。子集听起来很有趣。我还没有程序调用这个模块,所以修改会相对容易。 - Todd
嗨JJ,我没有看到“subset Icons of Str where * eq any <Exclamation Warning Information>;sub pop-up( Icons $icon ) { $icon}; say pop-up( "Information" ); ”。被“perl6 -c xxx.pm6”捕获,所以出了什么问题? - Todd
@Todd bding?你的意思是什么? - jjmerelo
嗨JJ,如果我在子声明中放置“where * ~~”,或者如果我进行子集操作,则检查器(-c)不会捕获到对子程序的错误调用,并且两者都等待在运行时被捕获。由于我只会使用那组命令一次,并且将其放置在子行中使得某人(目前是我)更容易找到参数,而不是四处寻找子集,因此我没有看到使用子集的实用性。(当事情重复时,我可以看到很多实用性。) - Todd

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