1

Rebol 中用于函数和闭包的低级原语是 FUNC 和 CLOS。如果没有明确告诉 FUNC 或 CLOS 进行本地化,那么分配将不是本地的。

x: 10
y: 20

foo: func [/local x] [
   x: 304
   y: 304
]

foo

print [{x is} x {and} {y is} y]

这将输出:

x 是 10,y 是 304

更高级别的例程 FUNCTION 和 CLOSURE 在默认库中编写为 Rebol 代码。他们在正文中扫描 SET-WORD 类别的符号(例如x:y:)。然后它们会自动生成一个增强的函数规范,将它们添加为 /LOCAL:

x: 10
y: 20

foo: function [] [
   x: 304
   y: 304
]

foo

print [{x is} x {and} {y is} y]

这将输出:

x 是 10,y 是 20

几乎所有时间都更好,所以这些名字得到更漂亮的名字是件好事。但是,您如何将 FUNCTION 用作对象成员?

bar: object [
    x: 10
    y: 20

    foo: function [] [
        x: 304
        y: 304
        c: 12-Dec-2012
        d: $0.50
    ]
]

这与在对象内的其他语言不同,默认情况下,成员不会被局部变量隐藏。如果某人想foo对对象中设置的任何单词都像 FUNC 一样行事,但对于不是的单词却像 FUNCTION 一样行事,该怎么办?

我唯一想到的就是传递self给 FUNCTION 代码的变体,例如:

method: func [
    me [object!] {always the parameter "self"?}
    spec [block!]
    body [block!]
] [
    unless find spec: copy/deep spec /local [append spec [
         /local
]]
    body: copy/deep body
    append spec exclude collect-words/deep/set/ignore body words-of me spec
    foreach l next find spec /local [
    if refinement? l [
        break
    ]
    insert body to-lit-word l
        insert body 'unset
    ]
    make function! reduce [spec body]
]

但是你将不得不写foo: method self [] [...]这是罗嗦的(假设这种方法甚至是合法的)

是否有任何技巧可以通过 pass inself或其他一些成语来支持这种愿望?还是每个人都只是使用 FUNC 作为对象成员?

4

3 回答 3

4

所描述的行为来自 rebol 中使用的动态范围。提议的定义:method从函数的 推断局部body变量,同时允许访问对象的实例变量,而无需程序员进行任何声明工作。在存在动态范围的情况下,这种类型的泄漏抽象是危险的。例如:

程序员编写了这个初始版本:

o: make object! [
   x: 1
   y: 1

   m: method [][
      x: 2
      y: 2
      z: x * y
   ]
]

多次修改后,另一位程序员决定将代码修改为:

o: make object! [
   x: 1
   y: 1

   z: method [][
      z: x + y
   ]

   m: method [][
      x: 2
      y: 2
      z: x * y
   ]
]

根据执行路径,修改后的代码可能会给出不同的结果。该方法的调用o/m将覆盖该o/z方法。因此,提议的实现引入了一个惊喜元素。

通过节省程序员清晰表达其意图的努力,代码变得脆弱。您可以明确表示您想要对象的成员,只需使用selfwhen 这就是您的意思:

o: make object! [
   x: 1
   y: 1

   z: function [][
      z: x + y
   ]

   m: function [][
      self/x: 2
      self/y: 2
      z: x * y
   ]
]

然后,您可以使用 FUNCTION 和 CLOSURE,它是可读且明确的。

于 2013-09-28T03:59:29.870 回答
2

这目前有效,但可能并不完全符合您的意愿:

bar: object [
    x: 10
    y: 20

    foo: function/with [] [
        x: 304
        y: 304
        c: 12-Dec-2012
        d: $0.50
    ] self
]
于 2013-09-28T04:34:30.970 回答
2

免责声明:我写了function

其余的答案已经很好地涵盖了标准行为,所以让我们跳过它。

让我们看看您对method函数的建议,以便更轻松地编写引用对象单词的对象绑定函数。通过一些调整,它可能是对 Rebol 的有用补充。

问题

以正常、正确的方式做事并不总是奏效。从 jvargas 获取一些代码:

o: make object! [
   x: 1
   y: 1

   z: function [][
      z: x + y
   ]

   m: function [][
      self/x: 2
      self/y: 2
      z: x * y
   ]
]

假设您需要保护单词x并防止y从对象外部访问 - 这是人们首先编写方法的主要原因之一。Rebol 提供了一个protect/hide功能。所以让我们使用它:

protect/hide/words in o [x y]

一旦你隐藏了这些词,它们就不能被访问,除非通过在它们受到保护之前绑定到它们的词——我们仍然希望现有的绑定能够工作,这样我们就可以编写允许访问这些词的特权代码,但想要阻止外部代码。因此,任何尝试访问这些单词的新代码或“外部”代码都会失败。o/x在这种情况下,外部代码意味着通过路径表达式(如)或通过绑定表达式(如 )引用单词in o 'x

不幸的是,对于上面的代码,这意味着self/xandself/y表达式也不起作用。如果他们这样做了,那么绕过这样的限制就太容易了:

do in o [self/x: "something horrible"]

这就是我们制作 的原因之一function/with,所以它可以像 Ladislav 建议的那样使用。但function/with也不一定能工作,因为它也意味着能够从外部工作 - 它显式地将函数体绑定到提供的对象,这增加了它在高级代码中的功能,但如果这些话在这里对我们没有帮助在构造方法之前被隐藏:

bar: object [
    x: 10
    y: 20
    protect/hide/words [x y]

    foo: function/with [] [
        x: 304
        y: 304
        c: 12-Dec-2012
        d: $0.50
    ] self
]

调用function/with不会保留xand y,因为它看不到它们(它们已经隐藏),所以这些词对于结果函数来说是本地的。要执行您想要的操作,您必须使用另一个选项:

bar: object [
    x: 10
    y: 20
    protect/hide/words [x y]

    foo: function/extern [] [
        x: 304
        y: 304
        c: 12-Dec-2012
        d: $0.50
    ] [x y]
]

这只是function跳过添加xy到本地,并且由于它们之前与对象的其余代码块绑定到对象,因此在隐藏单词之前,它们仍然会被绑定并且仍然有效。

这太棘手了。我们可以从一个简单的替代方案中受益。

一个办法

这是您的函数的清理版本,method可以解决一些问题:

method: func [
    "Defines an object function, all set-words local except object words."
    :name [set-word!] "The name of the function (modified)"
    spec [block!] "Help string (opt) followed by arg words (and opt type and string)"
    body [block!] "The body block of the method"
] [
    unless find spec: copy/deep spec /local [append spec [
         /local
    ]]
    body: copy/deep body
    append spec collect-words/deep/set/ignore body
        append append copy spec 'self words-of bind? name
    set name make function! reduce [spec body]
]

你像这样使用它:

bar: object [
    x: 10
    y: 20

    method foo: [] [
        x: 304
        y: 304
        c: 12-Dec-2012
        d: $0.50
    ]
]

您可能会注意到,这看起来比function/withor your更尴尬method,几乎就像它的语法一样。您可能还注意到您不必通过self或单词列表 - 那么它是如何工作的,因为 Rebol 没有范围?

诀窍在于,此函数使您将方法名称的 set-word 放在 word之后method而不是之前。这就是使它看起来像其他编程语言中的方法语法的原因。然而,对我们来说,它把方法名变成了method函数的参数,有了这个参数,我们就可以通过单词的绑定得到原始对象,然后从中得到单词列表。

还有一些其他因素可以使这种绑定技巧起作用。通过命名此函数method并在文档字符串中提及对象,我们几乎可以确保此函数通常仅用于对象或模块中。对象和模块从它们的代码块中的集合词中收集它们的词。通过确保名称必须是一个集合词,这使得它可能被绑定到对象或模块。我们声明它:name以阻止将 set-word 视为赋值表达式,然后在函数中显式设置它,就像人们可能天真的期望的那样。

作为 , 的一个优势function/withmethod它不会将函数体重新绑定到对象,它只是跳过对象的单词 likefunction/extern并留下现有的绑定。专业化让它变得更简单,并且有更少的开销作为奖励。

新的method也比原来的有几个优点method

  • 该循环中的额外代码具有在函数foreach中保留单词的副作用,以及取消设置单个细化组的本地单词的效果,而这些单词通常是默认的。函数本地词故意默认为 none,这会使它们的行为不一致。这是不必要的,也是不明智的。unsetnone
  • 这个self词很特殊,words-of如果你尝试分配它,它不会被返回并显式触发错误,以帮助你避免错误。您的代码丢失了该错误,其中的代码function 旨在保留它。考虑到意外覆盖是多么糟糕的想法self,最好要求人们通过在函数 locals 中声明来明确覆盖它。
  • exclude不适用于具有嵌套块的块,因此您的代码不适用于声明了类型的函数规范。这就是为什么首先function使用这些append调用。

这是否符合您的目的?

于 2014-02-15T00:12:59.733 回答