Rebol:块字的动态绑定

问题描述:

在Rebol中,有像foreach这样的字,它允许在给定单词和一系列(例如foreach w [1 2 3] [print w])上执行“块参数化”。由于我发现语法非常方便(与传递func块相反),我想用它来处理懒惰列表上的我自己的单词,例如map/stream x s [... x ... ]。 这个语法成语怎么叫?它如何正确实施?Rebol:块字的动态绑定

我正在搜索文档,但我找不到直接的答案,所以我试图自己实现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. 在绑定-VAR,我不得不做一些包装中bind [(block)] (:qw)因为(block)会“解散”。为什么?

  2. 由于2(?),则绑定操作一个新块(由[(block)]表达式创建的),不是原来的一个传递给我-的foreach,与单独的绑定上执行的,所以我要在该操作。我错误地加了[(block)],它仍然有效。但为什么?

+0

如果这是你感兴趣的话题,我绝对建议加入[讨论论坛](https://forum.rebol.info/t/separating-parse-rules-across-contexts/313/4 ?u = hostilefork)或[聊天室在这里](https://chat.stackoverflow.com/rooms/291/rebol)! – HostileFork

+1

谢谢你的邀请(你的伟大的答案,你甚至发现我的编辑;))!我对这些事情非常感兴趣,所以我会加入你。再次感谢! – ftl

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

(即Ren-C启动的原因是为了解决这样的顾虑。Progress has been madeseveral areas,虽然在写仍然有许多优秀的设计问题的时候,我会然而尝试只是回答下的历史假设你的问题。)

在绑定-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这里,否则将无法工作...定期撰写嵌入式PAREN!小号咳嗽,GROUP!小号[(block)]不会被替代。

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

如果你喜欢my-foreach x [1] [print x probe bind? 'x]测试的bind?的输出会告诉你,它必将进入“全局”用户上下文。

从根本上说,您没有任何制作对象!或USE来创建一个新的上下文来绑定身体。因此,所有你可能可能在这里做会剥离代码中的任何现有的绑定,并确保它们进入用户上下文。

但是原来你确实有一个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自己做的工作。 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只创建一个一个新的区块!最高级别是新的,在源材料中引用的“更深的”嵌入式模块将原有的材料别名:

>> 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 
] 

在一般笔记的效率,你在你的循环的每个迭代运行撰写和绑定操作。无论这些问题的创新解决方案如何得到(Ren-C中有很多新技术影响你的问题),你仍然可能只想做一次,然后重复使用它迭代。