我想生成序列
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
。
因此,序列在1
和0.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"(隐式或显式)来形成WhateverCode
lambda:
1/1, 1/2, 1/3, 1/(*.denominator+1) ... *
您可以删除此表单的牙套,这是它的优点之一。(您还可以在单个表达式中使用多个"whatever",而不仅仅是一个"it",这是此构造魅力的另一部分。
这种构造通常需要一些时间来适应;也许最大的障碍是*
必须与"WhateverCode
"运算符/函数相结合才能形成WhateverCode
lambda。
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/++$} ... *