我尝试在raku OOP中编写杨彦瞻的raku谜语比赛解决方案。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
来比较数字
当它可能只是$tigers %% 2
时,计算两次除以2
当有人做这样的事情时,真的会让人怀疑是否有更大的结构性问题碰巧解决了。
@!encounters
处于不一致状态这一事实对事情没有帮助。
我确实理解有人可能会这样编码。事实上,我确信我早期也有同样糟糕的代码。
如果我要尝试,我会删除基于对象的一个,并首先开始清理子程序。我的想法是,通过这样做,我会更好地了解问题空间。
我还必须编写大量的测试用例,以确保它适用于4
以外的数字
目前,这是我所知道的唯一有效的输入
实现中没有任何内容会让人尖叫:"这显然是正确的">
(就像我之前基本上说的,有些事情暗示它可能是不正确的。(
然后我可以利用这些知识来尝试将其重组为更容易转移到基于对象的设计中的东西。
或者我可以使子程序的内部值是基于对象的,从而慢慢地将其转移到基于对象的设计中。
假设这只是一个尝试用另一种方式编写它的练习,我建议在尝试将其对象化之前,先对子程序进行更多的处理。例如,您可以尝试使其不使用.flat
。