为什么重复中的循环变量不起作用

问题描述

repeat 的循环变量可以像这样在它的块中使用:

>> b: func [x] [x + i]
== func [x][x + i]
>> repeat i 2 [print reduce append copy [b] [3]]
4
5

您可以看到变量“i”被函数“b”使用。

然而,在下面的例子中,函数看不到循环变量“idx”。给出错误信息:

*** Script Error: idx has no value


Red []
map: function [a-func a-block][
result: [] args: [] clear result
either parse a-block [some block!][
  repeat idx length? a-block/1 [
    clear args
    foreach x a-block [append args to-block x/:idx]
    append result reduce append copy [a-func] args
    ]
   return result 
  ]
  [
   repeat idx length? a-block [
     append result to-block a-func a-block/:idx
     ]
   ]
]

map func [x y] [x + y - idx] [[1 2] [3 4]]

为什么第二个代码错误的?以及如何让它发挥作用?

解决方法

repeat 的循环变量可以像这样在它的块中使用

不幸的是,这在概念上是错误的。 块中不使用循环“变量”,它对函数的主体块和repeat都是“全局可见的”。

>> repeat index 5 []
>> index
== 5

从图表上看,它是 lambda → <idx in global context> ← repeat,而不是您可能认为的 lambda → repeat → <idx in repeat's "scope">

然而,在下面的例子中,函数看不到循环变量“idx”。

这是因为您使用了 function 构造函数,它使 idx 字在其上下文中局部化,如下所示:

>> function [][repeat index 3 []]
== func [/local index][repeat index 3 []]

相比之下,func(在您的第一个示例中使用)不会这样做。

>> func [][repeat index 3 []]
== func [][repeat index 3 []]

也就是说,在这段代码中:

map func [x y] [x + y - idx] [[1 2] [3 4]]

idx 在块上映射的匿名函数和 idx 实现中的 map 是两个完全不同的“变量”,绑定到不同的上下文:一个到全局(在它没有值,因此是错误消息),另一个是本地(默认设置为 none)。

Red 的“范围界定”模型的机制(或者说完全没有)是一个有点高级的话题,不过如果需要,我可以详细说明。

可以说它不依赖于传统的词汇范围(如大多数 Lisp 方言),也没有严格意义上的变量。相反,它依赖于符号值(又名单词),这些值带有与命名空间(又名上下文)的绑定,可以在运行时随意更改(参见下面示例中的 bind)——有点像 f - 表达式在 Kernel 和较旧的 Lisps 中,或者可能是照应宏,其中 collect(也见下文)是一个主要示例:注意它“捕获”keep 单词,从那时起on 指的是它的内部上下文,它被定义为 append 的临时别名。查看 source collect 输出以了解我的意思。


这是 map 的草图(技术上更像 zip,但无论如何)为您提供潜在解决方案的提示。

map: function [
    function [function!]
    series   [series!]
][
    spec:  spec-of :function ; ideally needs to be cleaned up
    step:  length? spec
    index: 1
    
    bind body-of :function 'index
    
    collect [
        foreach :spec series [
            keep/only do compose [(:function) (:spec)]
            index: index + step
        ]
    ]
]

示例:

>> map func [x] [reduce [index x]][a b c d]
== [[1 a] [2 b] [3 c] [4 d]]
>> map func [x y] [reduce [index x + y - index]][9 2 1 4]
== [[1 10] [3 2]]