检查列表中的所有元素是否在 Raku 中都是质数

Check if all elements of list are prime in Raku

my @g = (1,2,3,4);
say reduce {is-prime}, @g; # ==> gives error
say reduce {is-prime *}, @g; #==> gives error
say reduce {is-prime}, (1,2,3,4); # ==> gives error
say so is-prime @g.all; # ==> gives error

如何在 Raku 中检查列表中的所有元素是否都是素数?

您基本上是在问:此列表中是否有任何元素 不是 素数?我会写成:

say "not all prime" if @g.first: !*.is-prime;

请注意,根据 is-prime 函数,1 显然 不是 素数:

say 1.is-prime;  # False

因此 first 会在您的示例中的 1 上触发,而不是在 4.[=14= 上触发]

当然有很多方法可以做到这一点。一种非常明确的方法是使用 for 循环:

for @g -> $g {
 if $g.is-prime {
  say $g;
 }
}

或使用 grep(您可以隐式保留 $_):

@g.grep({ $_.is-prime }).say

以上都是假设你真的想过滤掉素数。当然你也可以真正检查每个数字并得到一个布尔值:

@g.map({ .is-prime }).say

使用 all 连接点:

say so all @g».is-prime; # False

上面的回答都很有帮助,但是它们无法解释为什么你的解决方案不起作用。基本上是 reduce is not going to apply a function (in your case, is-prime) to every member of a list. You want map。错误说

Calling is-prime() will never work with signature of the proto ($, *%)

因为reduce需要一个中缀,即二元函数,或者一个有两个参数的函数;它所做的是将它们应用于第一对元素,然后应用于结果和第三个元素,依此类推。由于类似的原因,最后一条语句不起作用:您使用列表参数调用 is-prime,而不是单个参数。

这个问题很大:

say reduce {is-prime}, @g;

您创建了一个 lambda:

{  }

它唯一做的就是调用一个函数:

is-prime

虽然你没有给函数任何参数。
它只是应该猜测参数应该是什么吗?

如果您打算传入 is-prime 作为参考,您应该使用 &is-prime 而不是 {is-prime}。 当然那还是不行。

另一个问题是 reduce 通过递归组合值来操作。
如果它一次对一个参数进行操作,它就无法做到这一点。
裸块 lambda {} 采用零个或一个参数,而不是两个或更多。


reduce 通常与 map.

结合使用

这种情况经常发生,以至于有一个关于 MapReduce 的维基百科页面。

say ( map &is-prime, @g ==> reduce { $^a and $^b } );
# False

say ( map &is-prime, 2,3,5 ==> reduce { $^a and $^b } );
# True

我是这样写的,这样 map 就会在 reduce 之前的那一行,但这样写可能会更清楚:

say reduce {$^a and $^b}, map &is-prime, 2,3,5;
# True

reduce 带有中缀运算符非常常见,因此有一种更短的写法。

say [and] map &is-prime, 2,3,5;
# True

当然最好只找到第一个不是质数的值,然后说它的倒数。

因为即使有一个值不是质数,也意味着它们不可能都是质数。

不过你必须要小心,因为你可能认为这样的事情总是有效的:

not @g.first: !*.is-prime;

它确实适用于您给它的值,但可能并非总是如此。
first returns Nil 如果找不到值。

not (2,3,5).first: !*.is-prime;
# not Nil === True

not (2,3,4).first: !*.is-prime;
# not 4   === False

not (2,3,0,4).first: !*.is-prime;
# not 0   === True

最后一个 returned 0not returns True.

组合

您可以使用 defined 来解决这个问题。

not defined (2,3,0,4).first: !*.is-prime;
# False

这仅在 first 不会 return 恰好在列表中的未定义元素时有效。

(Int,Any).first: Real
# Int

defined (Int,Any).first: Real
# False

您可以通过请求索引而不是值来解决这个问题。
你当然还需要 defined.

(Int,Any).first: :k, Real
# 0

defined (Int,Any).first: :k, Real
# True

另一种修复方法是使用 grep.

not (2,3,0,4).grep: !*.is-prime;
# not (0,4) === False

因为 grep 总是 return 是 List,您不必担心检查 0 或未定义的元素。
(如果 List 包含任何元素,则它是 True,无论值是什么。)

grep 足够聪明,知道如果您强制 Bool 它可以在找到第一个值时停止。 所以它短路就像你使用 first.


这会产生一些相当古怪的代码,其中包含这两个取反运算符。所以应该放到一个函数里面。

sub all-prime ( +@_ ) {
  # return False if we find any non-prime
  not @_.grep: !*.is-prime
  # grep short-circuits in Bool context, so this will stop early
}

如果你给它一些奇怪的东西,这仍然会失败

all-prime 2,3,5, Date.today;
# ERROR: No such method 'is-prime' for invocant of type 'Date'

如果您关心,请添加一些错误处理。

sub all-prime ( +@_ ) {
  # return Nil if there was an error
  CATCH { default { return Nil }}

  # return False if we find any non-prime
  not @_.grep: !*.is-prime
}

all-prime 2,3,5, Date.today;
# Nil