首页 > 解决方案 > 是否可以链接元运算符来计算 Raku 中两个列表的乘积和?

问题描述

我正在学习 Raku,有些事情让我感到困惑。

为了计算两个列表的元素乘积之和(至少其中一个是有限的),我希望能够写出类似的东西

@a Z*[+] @b

但它没有给出预期的结果。

使用[+] @a Z* @b表单按预期工作

> my @a = 2...6;
[2 3 4 5 6]
> my @b = 5...1;
[5 4 3 2 1]
> @a Z @b;
((2 5) (3 4) (4 3) (5 2) (6 1))
> @a Z* @b;
(10 12 12 10 6)
> [+] @a Z* @b
50

Z*但是当我将and链接[+]在一起时它不喜欢它

> @a Z*[+] @b
(30)
> @a Z*+ @b                           
(10)
> @a +Z* @b
===SORRY!=== Error while compiling:
Undeclared name:
    Z used at line 1

> @a +[Z*] @b
6
> @a [Z*]+ @b
(10)
> @a [Z*][+] @b
(30)

我想这里有一些优先级问题,但在 Raku 文档中找不到足够的示例。

我还发现了无法解释的行为>>*<<

> [+] @a >>*<< @b
50
> @a [+][>>*<<] @b
125
> @a [>>*<<][+] @b
Lists on either side of non-dwimmy hyperop of infix:<*> are not of the same length while recursing
left: 5 elements, right: 1 elements
  in block <unit> at <unknown file> line 1

问题

标签: operatorsraku

解决方案


是否可以以这种方式评估两个列表的积和?

是的。

让我们从链接开始:

  • 可以编写从右到左链接的 Raku 代码,但我们将坚持从左到右。

  • 你所谓的“产品总和”的从左到右的英文阅读将是“这些列表元素的产品总和。所以我们想在左边求和,下一个产品,最后一个列表。

有内置sum但没有内置product,所以我们需要处理后者。一种方法是写[Z*]归约(这意味着它必须出现在它归约的列表的左侧)。另一个是编写一个新product函数。

综上所述,以下是我们可以编写“乘积总和”的一些方法:

say [+] [Z*]             @a, @b;  # 50  (closest to what it seems you want)
say sum [Z*]             @a, @b;  # 50  (`sum` is idiomatic)
say sum zip :with(&[*]), @a, @b;  # 50  (some prefer `zip :with(&[*])` over `[Z*]`)
say sum product          @a, @b;  # 50  (requires a user defined `sub`)

sub product (|lists) { zip :with(&[*]), |lists }

这样做意味着您可以使用任意数量的列表,而不仅仅是两个。

如果您真的想坚持使用中缀表示法,这将您限制在两个列表中,您可以对产品部分执行此操作:

say [+] @a  Z*   @b;      # 50    (using infix sequential shallow zip metaop)
say [+] @a >>*<< @b;      # 50    (using infix parallel nesting hyper metaop)

say sum @a  Z*   @b;      # 50    (replacing `sum` with `[+]` reduction)
say sum @a >>*<< @b;      # 50

不同的链式变体发生了什么导致结果如6,(10)和?(30)125

Raku 正在正确地执行您的代码所表达的内容。

以下代码示例解释了@Zaid 共享的所有代码/结果。你(任何读者,不仅仅是@Zaid)可能需要做一些工作来理解为什么;如果您无法弄清楚这些示例如何解释 Zaid 的一个或多个结果,请发表评论,我很乐意在评论中解释和/或更新此答案,并感谢您的问题/评论。

say my @a = 2...6;          # [2 3 4 5 6]
say my @b = 5...1;          # [5 4 3 2 1]


say [+] [5,4,3,2,1];        # 15
                            # Same as:
say sum @b;                 # 15


say 2           Z* 15;      # (30)
                            # zip stops once shortest list exhausted, so same as:
say [2,3,4,5,6] Z* 15;      # (30)


say +@b;                    # 5
                            # `+` coerces argument(s) to number, so short for:
say elems @b;               # 5
                            # Same as:
say elems [5,4,3,2,1];      # 5


say 2 Z* 5;                 # (10)


#say +foo;                  # Error while compiling: Undeclared ... foo
                            # Same effect as:
#say foo;                   # Error while compiling: Undeclared ... foo


say [Z*] @b;                # (120)
                            # Same as:
say 5 Z* 4 Z* 3 Z* 2 Z* 1;  # (120)


say @a [Z*] 5;              # (10)
                            # square brackets redundant, and
                            # zip stops once shortest list exhausted, so same as:
say 2   Z*  5;              # (10)


say [+] @a >>*<< @b;        # 50


say [>>*<<] @b;             # 120
                            # hypers redundant if both args scalars, so same as:
say   [*]   [5,4,3,2,1];    # 120
                            # (5 * 4 * 3 * 2 * 1)


say @a [+] [>>*<<] @b;      # 125
                            # square brackets round infix `+` redundant, so same as:
say @a  +  [>>*<<] @b;      # 125
                            # hypers redundant if both args scalars, so same as:
say 5   +    [*]   @b;      # 125      (5 + 120)


say @a [>>*<<][+]  @b;      # Same as:
say @a [>>*<<] [+] @b;      #
say @a [>>*<<] sum @b;      #
say @a  >>*<<  sum @b;      #
#say @a  >>*<<  15;         # Lists on either side ... not of the same length

推荐阅读