递归函数中的 Raku 类型约束和 类

Raku type constraints in recursion functions and classes

我曾尝试编写 yangyanzhan's solution of the raku-riddle-contest in raku OOP. Raku class system is very intuitive, and everything work as a charm,直到遇到递归函数。这是 class 和函数的代码版本:

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)]

另一方面,class总是陷入无限循环。我相信函数和 class 之间的区别在于这行代码:

 @!encounters = ( @!encounters, [$tiger]).flat;
 my $sig = ( @encounters, [$tiger] ).flat;

我不清楚这是因为递归语法格式错误还是函数与 classes 之间类型约束的差异。

好吧,它们不是同一种算法也无济于事。
他们很接近,但不一样。

在你的子程序中:

my $sig = ( @encounters, [$tiger] ).flat;

但该方法改为修改属性:

@!encounters = ( @!encounters, [$tiger]).flat;

我真的不知道当一个不修改其参数但另一个修改时您如何期望它以相同的方式运行。


不仅如此,子例程一使用 Seq 个值。

my $sig = ( @encounters, [$tiger] ).flat;
note $sig.raku;
# $((2, 4, 2, 2).Seq)

而方法一使用数组。


我看不出 class 有任何好处。

像您拥有的那样修改 class 结构的方法调用不是一个好主意。
我会在对象构建时进行更多处理。


我不会尝试修复它或重写它,因为有一些简单的决定让我质疑解开设计需要多长时间。

来自使用字符串比较 eq 来比较数字。
在本来可以 $tigers %% 2

的情况下计算两次除以 2

当有人做那样的事情时,真的会怀疑是否没有恰好解决的更大的结构性问题。

@!encounters 处于不一致状态这一事实无济于事。

我确实理解有人可能会这样编码。事实上,我确信我在早期的代码中也有同样糟糕的代码。


如果我要尝试,我会先删除基于对象的,然后开始清理子例程。 我的想法是,通过努力,我可以更好地了解问题 space。

我还必须编写大量测试用例以确保它适用于 4 以外的数字。
现在,这是我知道它适用的唯一输入。
实现中没有任何东西尖叫:“这显然是正确的”。
(就像我之前所说的那样,有些事情暗示它可能不正确。)

然后我可以利用这些知识尝试将其重组为可以更轻松地转移到基于对象的设计中的东西。

或者我可能会让子例程的内部值是基于对象的,从而慢慢地将它转移到基于对象的设计中。


假设这只是尝试以另一种方式编写它的练习,我会建议在尝试将其对象化之前,多弄乱子例程。 例如,您可以尝试使其不使用 .flat.