如何生成惰性除法?

How to generate a lazy division?

我要生成

的序列
1, 1/2, 1/3, 1/4 ... *

在 raku 中使用函数式编程方法,在我看来它应该是这样的:

(1,{1/$_} ...*)[0..5]

但输出是:1,1,1,1,1 这个想法很简单,但似乎足以让我用来生成其他复杂列表并使用它。

我尝试的其他事情是使用惰性列表在其他惰性列表中调用,它也不起作用,因为输出是一个重复序列:1, 0.5, 1, 0.5 ...

my list = 0 ... *;
(1, {1/@list[$_]} ...*)[0..5]

查看@wamba 的精彩解答,了解您标题中问题的解答。他们展示了广泛适用的 Raku 结构。

此答案主要关注 Raku 的序列运算符 (...),以及您问题 body 中的详细信息,解释您的尝试中出现的错误,并解释一些工作序列。

TL;DR

N 项的值为 1 / N

# Generator ignoring prior terms, incrementing an N stored in the generator:
{ 1 / ++$ } ... *                               # idiomatic
{ state $N; $N++; 1 / $N } ... *                # longhand

# Generator extracting denominator from prior term and adding 1 to get N:
1/1, 1/2, 1/3, 1/(*.denominator+1) ... *        # idiomatic (@jjmerelo++)
1/1, 1/2, 1/3, {1/(.denominator+1)} ... *       # longhand (@user0721090601++)

{1/$_} 怎么了?

1, 1/2, 1/3, 1/4 ... *

N 项的值是多少?这是 1/N.

1, {1/$_} ...*

N 项的值是多少?这是 1/$_.

$_ 是通用的 parameter/argument/operand 类似于英语代词“it”。

是否设置为N

没有

因此您的生成器 (lambda/function) 不会对您尝试重现的序列进行编码。

$_ 设置了什么?

在函数中,$_ 绑定到 (Any),或绑定到传递给函数的参数。

如果一个函数明确指定了它的参数(一个“参数”指定了一个函数期望接收的实参;这是不同的从一个函数实际上最终获得任何给定调用的参数),然后 $_ 根据该规范绑定或不绑定。

如果一个函数没有明确指定它的参数——而你的没有——然后$_被绑定到参数,如果有的话,那就是作为函数调用的一部分传递。

对于 generator 函数,作为参数传递的任何值都是 序列中前面项的值

鉴于您的生成器未明确指定其参数,如果有的话,紧接在前的术语将被传递并绑定到 $_

在生成器的 first 调用中,当 1/$_ 被求值时,$_ 从第一个开始就绑定到 1学期。所以第二项是1/1,即1.

因此,产生第三项的 second 调用具有相同的结果。所以你得到一个无限的 1s.

序列

{1/@list[$_+1]}怎么了?

对于你的最后一个例子,你大概是指:

my @list = 0 ... *;
(1, {1/@list[$_+1]} ...*)[0..5]

在这种情况下,生成器 returns 1/@list[1+1] 第一次 调用是 1/2 (0.5)。

所以 第二个 调用是 1/@list[0.5+1]。这指定 @list 的小数索引,要求第 1.5 个元素。标准 Positional 的索引向下舍入到最接近的整数。所以 1.5 向下舍入为 1@list[1] 的计算结果为 1。所以第二次调用生成器返回的值又回到了1.

因此序列在 10.5 之间交替。

传递给生成器的参数是什么?

Raku 将序列中零个或多个先验项的值作为参数传递给生成器。

有多少?好吧,发电机就是普通的 Raku lambda/function。 Raku 使用参数的隐式或显式声明来确定要传递多少参数。

例如,在:

{42} ... * # 42 42 42 ...

lambda 没有声明它有什么参数。对于此类函数,Raku 假定签名包括 $_?,因此通过了先前的术语(如果有的话)。 (上面的 lambda 忽略它。)

need/want你的生成器要传递哪些参数?

有人可能会争辩说,对于您打算生成的序列,您不会 need/want 传递 任何 的先验项。因为,可以说,none 确实很重要。

从这个角度来看,重要的是第 N 项计算 1/N。也就是说,它的值独立于先验项的值,仅取决于计算调用次数

陈述解决方案,例如{1/++$}

一种计算方法如下:

{ state $N; $N++; 1/$N } ... *

lambda 忽略前一项。最终结果正是我们想要的 1 1/2 1/3 ....

(除非你必须 fiddle 进行字符串化,因为默认情况下它会使用 gist 这会将 1/3 变成 0.333333 或类似的.)

或者,更多 succinctly/idiomatically:

{ 1 / ++$ } ... *

(statement/expression 中的匿名 $ 是同时声明和使用匿名状态标量变量。)

使用先验项的解决方案

正如@user0721090601++ 在下面的评论中指出的那样,可以编写一个利用先验值的生成器:

1/1, 1/2, 1/3, {1/(.denominator+1)} ... *

对于没有明确指定参数的生成器,Raku 将序列中前一项的值作为参数传递,将其绑定到“it”参数 $_.

鉴于 .denominator 没有明确的调用者,Raku 假定您打算在 $_.

上调用该方法

正如@jjmerelo++ 所指出的,一种惯用的表达方式许多 lambda 是使用显式代词“whatever”而不是“it”(隐式或显式)来形成 WhateverCode lambda:

1/1, 1/2, 1/3, 1/(*.denominator+1) ... *

你去掉了这个表格的大括号,这是它的优点之一。 (你也可以在一个表达式中使用多个“whatevers”而不是一个“it”,这是这个结构的另一个魅力所在。)

这个构造通常需要一些时间来适应;也许最大的障碍是 * 必须与“WhateverCodeable”operator/function 组合才能形成 WhateverCode lambda。

TIMTOWTDI

routine map

(1..*).map: 1/*

List repetition operator xx

1/++$ xx *

The cross metaoperator, X or the zip metaoperator Z

1 X/ 1..*
1 xx * Z/ 1..*

(Control flow) control flow gather take

gather for 1..* { take 1/$_ }

(Seq) method from-loop

Seq.from-loop: { 1/++$ }

(Operators) infix ...

1, 1/(1+1/*) ... *
{1/++$} ... *