递归函数和类中的Raku类型约束
我已经尝试在raku OOP中编写了yangyanzhan的raku-riddle-contest解决方案。Raku 类系统非常直观,在我遇到递归函数之前,一切都很有趣。这是类和函数的代码版本:
class Encounters {
has $.tigers;
has @!encounters;
method encounters {
if @!encounters.elems eq $.tigers {
return [@!encounters, ];
}
my @total_encounters = [] ;
for 1..$.tigers -> $tiger {
if ($tiger / 2) eq ($tiger / 2).round {
@!encounters = ( @!encounters, [$tiger]).flat ;
my @encounter_events = Encounters.new( tigers => $.tigers, encounters => @!encounters ).encounters;
@total_encounters.append: @encounter_events;
}
}
return @total_encounters;
}
}
sub encounters($tigers, @encounters) {
if @encounters.elems eq $tigers {
return [@encounters, ];
}
my @total_encounters = [] ;
for 1..$tigers -> $tiger {
if ($tiger / 2) eq ($tiger / 2).round {
my $sig = ( @encounters, [$tiger] ).flat;
my @encounter_events = encounters( $tigers, $sig );
@total_encounters.append: @encounter_events;
}
}
return @total_encounters;
}
sub MAIN( $tigers ) {
(encounters $tigers, [] ).say;
Encounters.new( tigers => $tigers ).encounters.say;
}
对于 $tigers = 4,函数给出:
[(2 2 2 2) (2 2 2 4) (2 2 4 2) (2 2 4 4) (2 4 2 2) (2 4 2 4) (2 4 4 2) (2 4 4 4) (4 2 2 2) (4 2 2 4) (4 2 4 2) (4 2 4 4) (4 4 2 2) (4 4 2 4) (4 4 4 2) (4 4 4 4)]
另一方面,类总是陷入无限循环。我相信函数和类之间的区别在于这行代码:
@!encounters = ( @!encounters, [$tiger]).flat;
my $sig = ( @encounters, [$tiger] ).flat;
我不清楚这是因为递归语法格式错误还是函数和类之间的类型约束不同。
回答
好吧,它们不是相同的算法也无济于事。
它们很接近,但又不一样。
在子程序一中,您有:
my $sig = ( @encounters, [$tiger] ).flat;
但该方法改为使用以下方法修改属性:
@!encounters = ( @!encounters, [$tiger]).flat;
我真的不知道当一个不修改其参数但另一个修改它时,您如何期望它以相同的方式行事。
更重要的是,子程序使用Seq值。
my $sig = ( @encounters, [$tiger] ).flat;
note $sig.raku;
# $((2, 4, 2, 2).Seq)
而方法一使用数组。
我看不出你对班级有什么好处。
拥有一个像您拥有的那样修改类结构的方法调用并不是一个好主意。
我会在对象构建时进行更多的处理。
我不会尝试修复它或重写它,因为有一些简单的决定让我怀疑解开设计需要多长时间。
从使用字符串比较eq来比较数字。
在本来可以除以 2 的情况下计算两次$tigers %% 2
当有人做这样的事情时,真的让人怀疑是否有更大的结构性问题恰好解决了。
这事实上@!encounters是处于不一致的状态没有帮助的东西。
我确实理解有人可能会以这种方式编码。事实上,我确信我早期的代码同样糟糕。
如果我要尝试,我会删除基于对象的对象,然后首先开始清理子例程。我的想法是,通过努力,我会更好地了解问题空间。
我还必须编写大量测试用例以确保它适用于4.
现在,这是我知道它适用的唯一输入。
实现中没有任何东西会尖叫:“这显然是正确的”。
(就像我之前所说的,有些事情暗示它可能是不正确的。)
然后我可以使用这些知识尝试将其重组为可以更轻松地转移到基于对象的设计中的内容。
或者我可以让子程序的内部值基于对象,从而慢慢地将它转移到基于对象的设计中。
假设这只是尝试以另一种方式编写它的练习,我建议在尝试将其对象化之前,多弄乱一个子程序。例如,您可以尝试使其不使用.flat.