如何生成惰性除法?

met*_*gib 14 lazy-sequences raku

我想生成的序列

1, 1/2, 1/3, 1/4 ... *
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

rai*_*iph 14

请参阅@wamba 对标题中问题的解决方案的精彩回答。他们展示了广泛的适用 Raku 结构。

此答案侧重于 Raku 的序列运算符 ( ...),以及问题正文中的详细信息,解释了您尝试中出现的问题,并解释了一些工作序列。

TL; 博士

N第 th 项的值是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++)
Run Code Online (Sandbox Code Playgroud)

怎么了 {1/$_}

1, 1/2, 1/3, 1/4 ... *
Run Code Online (Sandbox Code Playgroud)

N第 th 项的值是多少?它是1/N

1, {1/$_} ...*
Run Code Online (Sandbox Code Playgroud)

什么是价值 N第 th 项多少?它是1/$_

$_ 是通用的 类似于英语代词“it”参数/参数/操作数

是否设置为 N

不。

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

什么是 $_设置?

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

如果一个函数显式地指定了它的参数(一个“参数”指定了一个函数期望接收的参数;这与一个函数实际上最终获得的任何给定调用的参数不同),那么$_是绑定或不绑定,每个那个规格。

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

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

鉴于您的生成器没有明确指定其参数,如果有的话,前一项将被传递并绑定到 $_

在您的生成器的第一次调用中,当1/$_被评估时,$_绑定到1来自第一项的 。所以第二项是1/1,即1

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

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

对于您的最后一个示例,您大概的意思是:

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

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

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

因此序列在1和之间交替0.5

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

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

多少?好吧,生成器是一个普通的 Raku lambda/函数。Raku 使用隐式或显式参数声明来确定要传递多少个参数。

例如,在:

{42} ... * # 42 42 42 ...
Run Code Online (Sandbox Code Playgroud)

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

需要/想要哪些参数您的生成器通过?

有人可能会争辩说,对于您要生成的序列,您不需要/不想通过任何先前的条款。因为,可以说,没有它们重要。

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

状态解决方案,例如 {1/++$}

计算这个的一种方法是这样的:

{ state $N; $N++; 1/$N } ... *
Run Code Online (Sandbox Code Playgroud)

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

(除了你必须摆弄字符串化,因为默认情况下它会使用gistwhich 将变成1/3into0.333333或类似的。)

或者,更简洁/惯用地:

{ 1 / ++$ } ... *
Run Code Online (Sandbox Code Playgroud)

$语句/表达式中的匿名是同时声明和使用匿名状态标量变量。)

使用前项的解决方案

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

1/1, 1/2, 1/3, {1/(.denominator+1)} ... *
Run Code Online (Sandbox Code Playgroud)

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

并且鉴于没有明确的 for.denominator调用者,Raku 假定您的意思是在 上调用该方法$_


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

1/1, 1/2, 1/3, 1/(*.denominator+1) ... *
Run Code Online (Sandbox Code Playgroud)

您可以放弃这种形式的大括号,这是它的优点之一。(您也可以在一个表达式中使用多个“whatevers”,而不仅仅是一个“it”,这是该结构魅力的另一部分。)

这种构造通常需要一些时间来适应;也许最大的障碍是 a*必须与“WhateverCode能够”的运算符/函数结合才能形成WhateverCodelambda。

  • 如果想要迭代地计算初始值,那么 `1/1, { 1 / (*.deminator + 1) } ... *` 也可以工作 (3认同)
  • @user0721090601,几乎,没有花括号:(1/1, 1 / (*.分母 + 1) ... *)[0..5] (2认同)
  • 我学到了很多东西,感谢您花时间解释我的代码有什么问题,它非常有用,并且认为您的精彩答案和@wamba 的答案是非常清晰和惯用的方法来完成更复杂的列表 (2认同)