使用本地收集FUNCTION而不是FUNC时访问对象成员

时间:2013-09-28 00:45:28

标签: object rebol scoping rebol3

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的角色,那么该怎么办?

我唯一想到的是将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 [] [...] 这是罗嗦(假设这种方法甚至是合法的)

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

3 个答案:

答案 0 :(得分: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方法。因此,所提出的实施引入了一个惊喜的元素。

通过保存程序员清楚地表达其意图的努力,代码变得脆弱。当你的意思是这样的时候,你可以明确地想要使用self来成为对象的成员:

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,它是可读且明确的。

答案 1 :(得分:2)

目前这种方法有效,但可能并不完全符合您的要求:

bar: object [
    x: 10
    y: 20

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

答案 2 :(得分: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
   ]
]

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

protect/hide/words in o [x y]

一旦你隐藏了这些单词,就无法访​​问它们,除非通过在它们受到保护之前绑定到它们的单词 - 我们仍然希望现有的绑定能够工作,这样我们就可以编写允许访问单词的特权代码,但想阻止外部代码。所以任何新的或"外面"尝试访问单词的代码将失败。在这种情况下,外部代码表示通过路径表达式引用单词,如o/x,或通过in o 'x之类的绑定表达式。

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

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

这是我们制作function/with的原因之一,因此它可以像Ladislav建议的那样使用。但是function/with不一定能够工作,因为它也意味着能够从外部工作 - 它明确地将函数体绑定到提供的对象,这增加了它在高级代码中的能力,但不是&# 39;如果在构造方法之前隐藏了单词,请帮助我们:

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的电话不会保留xy,因为它不会看到它们(它们已经隐藏),所以这些话会是结果函数的本地。要做你想做的事,你必须使用另一种选择:

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/with或你的method更难以尴尬,几乎就像它的语法一样。您可能还注意到,您不必通过self或单词列表 - 所以考虑到Rebol缺少范围,它是如何工作的?

诀窍是这个函数可以让你在单词method后面放置方法名的set-word,而不是它。这就是它看起来像其他编程语言中的方法语法。但是,对于我们来说,它将方法名称转换为method函数的参数,使用该参数,我们可以通过绑定单词来获取原始对象,然后从中获取单词列表。 / p>

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

作为优于function/with的优势,method不会将函数体重新绑定到对象,它只会跳过对象的function/extern之类的字词并离开现有的绑定。专业化让它变得更简单,并且作为奖励有更少的开销。

method与原始method相比还有一些优势:

  • foreach循环中的额外代码有副作用 在函数中保留单词unset,以及效果 在何时取消单个细化组的本地单词 通常这些单词默认为none。功能本地词 默认情况下没有任何意图,这将使他们的行为 不一致。这是不必要的,也是不明智的。
  • self字是特殊的,words-of并未返回 如果您尝试分配错误,则显式触发错误,以帮助您 避免错误。您的代码丢失了该错误,function中的代码 旨在保护它。考虑到它的想法有多糟糕 意外覆盖self,最好要求人们 通过在函数locals中声明它来显式覆盖它。
  • exclude不能处理嵌套块的块,所以你的 代码不会使用声明类型的函数规范。 这就是function首先使用append次调用的原因。

这是否符合您的目的?