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 reduce { $^a + $^b }, (1,2,3);
现在我们知道为什么它不起作用,因为 reduce
需要一个带有两个参数的函数,但 is-prime
只能操作一个参数。 - Lars Malmsteensay "not all prime" if @g.first: !*.is-prime;
say 1.is-prime; # False
first
会在你的示例中的 1 上触发,而不是在 4 上触发。with
而不是 if
。如果第一个值是 0
,会怎样呢? - Brad Gilbertwith
,也无法捕获实际元素是类型对象的情况,例如 (Int,Str,Date).first: { $_ ~~ Str }
。因此,您需要查看实际使用情况。由于我们不检查 0
是否为质数,所以 if
应该可以胜任 :-) - Elizabeth Mattijsengrep
比 first
更适合这种情况的原因。 - Brad Gilbertgrep
将始终测试所有元素,它不会短路。您可以通过使用:k
副词和with
来始终使用first
: say "found" with (Int,Str,Date).first(Int, :k); # found
- Elizabeth Mattijsen这个存在一个大问题:
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
我是这样写的,以便在reduce
之前一行中含有map
,但也许这样更清晰:
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
如果找不到该值,则返回 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
0
,当与not
组合时返回True
。您可以使用defined
来解决这个问题。not defined (2,3,0,4).first: !*.is-prime;
# False
first
不会返回一个恰好在列表中的未定义元素时才有效。(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
总是返回一个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
当然有很多种方法可以实现这个功能。一种非常明确的方式是使用for循环:
for @g -> $g {
if $g.is-prime {
say $g;
}
}
或者使用grep命令(可以隐式地保留$_):
@g.grep({ $_.is-prime }).say
以上两种方法都假设您真的想要过滤掉质数。当然,您也可以真正“检查”每个数字并获得布尔值:
@g.map({ .is-prime }).say
True
或 False
。这些示例仍然很有帮助,因为它们展示了如何正确地迭代列表。 - Lars Malmsteensay @g .grep: *.is-prime
。在我的第一个 SO 的答案中提到的 where
子句中,*
与 +*
中的相同。在这种情况下,你会得到(相同的) { $_.is-prime }
。.grep: ...
是 .grep(...)
的替代方法。如果你想/需要链式调用方法,就不能使用 .foo: ...
,但如果不需要,它通常很好用。 - raiph使用all
连接符:
say so all @g».is-prime; # False
all
函数(这里称为连接器还是方法?)来评估结果列表也是一个好主意。这真的很有帮助。 - Lars Malmsteenso
可以在遇到第一个 False
特征态之前,它将创建一个带有 所有 特征态的 Junction
。由于 .is-prime
可能非常昂贵,需要首先完成所有这些计算的事实可能会使这种方法的速度与 .first
解决方案相当,仅当最后一个特征态为 False
时。在所有其他情况下,first
方法可能会快几个数量级。 - Elizabeth Mattijsen@g.all.is-prime
,那么这个操作不就会转移到可短路的连接处吗?(我很惊讶没有人建议使用 @g.all.is-prime
,因为对我个人来说这是最易读的) - user0721090601so
中,技术上是在 Junction.Bool
中。为此,必须构建整个 Junction
。 - Elizabeth Mattijsen
so is-prime @g.all
而出现错误,尽管对我来说将其写作so is-prime all @g
或?@g.all.is-prime
更为合理。 - user0721090601