如何生成懒惰除法



我想生成序列

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 的序列运算符 (...),以及问题正文中的细节,解释您的尝试中出了什么问题,并解释了一些工作序列。

TL;博士

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/$_.

$_是一个通用参数/参数/操作数,类似于英语代词"it"。

是否设置为N

不。

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

$_设置了什么?

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

如果函数显式指定其参数("参数"指定函数期望接收的参数;这与函数实际最终为任何给定调用获得的参数不同),则$_根据该规范被绑定或不绑定。

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

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

假设生成器未显式指定其参数,则传递前一项(如果有)并将其绑定到$_

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

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

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

对于您的最后一个示例,您可能意味着:

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

在这种情况下,生成器的第一个调用返回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/函数。Raku 使用参数的隐式或显式声明来确定要传递的参数数。

例如,在:

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

lambda 不会声明它有什么参数。对于这样的函数,Raku假定一个包含$_?的签名,因此传递了前一项,如果有的话。(上面的 lambda 忽略了它。

您需要/希望传递生成器的哪些参数?

有人可能会争辩说,对于您要生成的序列,您不需要/不想传递任何先前的术语。因为,可以说,它们都不重要。

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

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

计算此值的一种方法是:

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

lambda 忽略前一项。最终结果只是所需的1 1/2 1/3 ....

(除了你必须摆弄字符串化,因为默认情况下它会使用gist,这会将1/3变成0.333333或类似

。或者,更简洁/惯用地:

{ 1 / ++$ } ... *

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

使用先前术语的解决方案

正如下面的注释中@user0721090601++所指出的那样,可以编写一个使用先前值的生成器:

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

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

鉴于没有显式调用.denominator,Raku 假定您的意思是在$_上调用该方法。


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

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

您可以删除此表单的牙套,这是它的优点之一。(您还可以在单个表达式中使用多个"whatever",而不仅仅是一个"it",这是此构造魅力的另一部分。

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

TIMTOWTDI

常规map

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

列表重复运算符xx

1/++$ xx *

交叉元运算符、X或 zip 元运算符Z

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

(控制流)控制流收集take

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

(顺序)方法from-loop

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

(运算符)中缀...

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

相关内容

  • 没有找到相关文章

最新更新