Rebol:块字的动态绑定

时间:2017-11-10 22:42:59

标签: rebol

在Rebol中,有像 foreach 这样的词允许对给定的单词和系列进行“块参数化”,例如foreach w [1 2 3] [print w]。由于我发现该语法非常方便(与传递func块相反),我想将它用于我自己在懒惰列表上运行的单词,例如map/stream x s [... x ... ]。 如何调用这种语法习惯用法?如何正确实施?

我正在搜索文档,但我找不到直接的答案,所以我试图自己实现foreach。基本上,我的实现分为两部分。第一部分是一个函数,它将块中的特定单词绑定到给定值,并生成带有绑定单词的新块。

bind-var: funct [block word value] [
  qw: load rejoin ["'" word]
  do compose [
      set (:qw) value
      bind [(block)] (:qw)
      [(block)] ; This shouldn't work? see Question 2
  ]
]

使用它,我按如下方式实现了foreach:

my-foreach: func ['word s block] [
    if empty? block [return none]
    until [
        do bind-var block word first s
        s: next s 
        tail? s
    ]
]

我发现这种方法非常笨拙(可能是这样),所以我想知道如何更优雅地解决问题。无论如何,在提出我的装置后,我有两个问题:

  1. 在bind-var中,我不得不在bind [(block)] (:qw)中进行一些包装,因为(block)会“解散”。为什么呢?

  2. 因为(?)为2,绑定操作是在一个新块(由[(block)]表达式创建)上执行的,而不是传递给my-foreach的原始块,具有单独的绑定,所以我必须对此进行操作。错误地,我添加了[(block)],它仍然有效。但为什么?

1 个答案:

答案 0 :(得分:4)

好问题。 :-)在Rebol2和R3-Alpha中编写自己的自定义循环结构(现在,历史重复为Red)有许多未解决的问题。 Rebol3开发人员和considered blocking bugs已知这些问题。

(启动Ren-C的原因是为了解决这些问题。Progress has been made several areas,但在撰写本文时仍存在许多优秀的设计问题。我会尝试然而,只是在历史假设下回答你的问题。)

  

在bind-var中,我必须在bind [(block)] (:qw)中进行一些包装,因为(block)会“解散”。为什么呢?

这就是COMPOSE默认工作的方式......而且它通常是首选的行为。如果您不想这样,请使用COMPOSE / ONLY,并且不会拼接块,而是按原样插入。

  

qw: load rejoin ["'" word]

你可以转换WORD! LIT-WORD!通过to lit-word! word。您也可以将报价责任转移到样板文件中,例如set quote (word) value,并完全避免qw

避免LOAD通常也是首选,因为它默认情况下会将内容带入用户上下文 - 因此它会丢失原始单词的绑定。执行TO转换将保留原始WORD的绑定!在生成的LIT-WORD中!。

do compose [
    set (:qw) value
    bind [(block)] (:qw)
    [(block)] ; This shouldn't work? see Question 2
 ]

大概你在这里意味着COMPOSE / DEEP,否则这根本不起作用...定期COMPOSE嵌入式PAREN!s 咳嗽,GROUP! s for [(block)]将不会被替换。

  

错误地,我添加了[(block)],它仍然有效。但为什么呢?

如果您执行my-foreach x [1] [print x probe bind? 'x]之类的测试,bind?的输出将显示它已绑定到“全局”用户上下文。

从根本上说,你没有任何制造对象!或USE创建一个新的上下文绑定身体。因此,潜在在此处所做的一切都将剥离x代码中的任何现有绑定,并确保它们进入用户上下文。

但最初你确实有一个USE,即edited to remove。这更像是在正确的轨道上:

bind-var: func [block word value /local qw] [
    qw: load rejoin ["'" word]
    do compose/deep [
        use [(qw)] [
            set (:qw) value
            bind [(block)] (:qw)
            [(block)] ; This shouldn't work? see Question 2
        ]
    ]
]

你是正确的,怀疑你的约束力有些歪斜。但这样做的原因是因为你的BIND只是重做USE本身所做的工作。已经深入使用以确保调整任何单词绑定。所以你可以完全省略绑定:

do compose/deep [
    use [(qw)] [
        set (:qw) value
        [(block)]
    ]
]
  

对新块(由[(block)]表达式创建)执行绑定操作,而不是传递给my-foreach的原始块,具有单独的绑定

让我们通过深度使用USE来调整您的代码,以展示您思考的问题。我们将使用一个简单的MAKE OBJECT!代替:

bind-var: func [block word value /local obj qw] [
    do compose/deep [
        obj: make object! [(to-set-word word) none]
        qw: bind (to-lit-word word) obj
        set :qw value
        bind [(block)] :qw
        [(block)] ; This shouldn't work? see Question 2
    ]
]

现在,如果你尝试my-foreach x [1 2 3] [print x],你会得到你所怀疑的......“x没有价值”(假设你没有一些潜在的全局定义x它会拾取,这只会打印出来相同的潜伏值3倍)。

但为了让你充分抱歉,你问:-),我会提到my-foreach x [1 2 3] [loop 1 [print x]]实际有效。那是因为虽然你说过去的绑定不应该影响新的块是正确的,但是这个COMPOSE只创建了一个新的BLOCK!最顶层是新的,源材料中引用的任何“更深”的嵌入块将是原始材质的别名:

>> original: [outer [inner]]
== [outer [inner]]

>> composed: compose [<a> (original) <b>]
== [<a> outer [inner] <b>]

>> append original/2 "mutation"
== [inner "mutation"]

>> composed
== [<a> outer [inner "mutation"] <b>]

因此,如果您对撰写的结果进行变异BIND,则会严重影响您的部分

until [
    do bind-var block word first s
    s: next s 
    tail? s
]

关于效率的一般说明,您在循环的每次迭代中运行COMPOSE和BIND操作。无论对这些问题有多么有创意的新解决方案(Ren-C中有很多新技术会影响你的问题),你仍然可能只想做一次并在迭代中重复使用它。