7

我通常以“本能”的方式通过函数进行编程,但我目前的问题可以通过对象轻松解决,所以我继续使用这种方法。

这样做,我试图找到一种方法给对象一个构造方法,例如,相当于 python 中的init ()。

我查看了http://www.rebol.com/docs/core-fr/fr-index.html文档,但找不到任何相关内容。

4

4 回答 4

7

Rebol 中没有特殊的构造函数,但是如果您在spec块中创建对象时需要它,可以编写临时初始化代码。例如:

a: context [x: 123]

b: make a [
    y: x + 1
    x: 0
]

因此,如果您按照约定在基础对象中定义自己的“构造函数”函数,则可以在创建时将其称为规范块。如果你想让它自动化,你可以将它包装在一个函数中,如下所示:

a: context [
    x: 123
    init: func [n [integer!]][x: n]
]

new-a: func [n [integer!]][make a [init n]]

b: new-a 456

一个更健壮(但更长一点)的new-a版本可以避免传递给init的参数可能与对象自己的话发生冲突:

new-a: func [n [integer!] /local obj][
    also 
        obj: make a []
        obj/init n
]

您还可以编写一个更通用的函数,它将基对象作为第一个参数,并在克隆对象后自动调用按约定构造函数,但是以通用方式支持可选构造函数参数则更加棘手。

请记住,Rebol 的对象模型是基于原型的(相对于 Python 和大多数其他 OOP 语言中的基于类),因此“构造函数”函数会为每个创建的新对象复制。如果您要创建大量对象,您可能希望避免这种成本。

于 2013-10-07T18:54:48.930 回答
5

据我所知,没有正式的方法/约定来使用对象构造函数,例如init(). 当然还有构建派生对象的内置方法:

make prototype [name: "Foo" description: "Bar"]
    ; where type? prototype = object!

我最好的建议是定义一个检查对象的构造函数方法的函数,然后应用该方法,这是我之前提出的一个这样的函数:

new: func [prototype [object!] args [block! none!]][
    prototype: make prototype [
        if in self 'new [
            case [
                function? :new [apply :new args]
                block? :new [apply func [args] :new [args]]
            ]
        ]
    ]
]

用法很简单:如果原型对象有new值,那么它将应用于派生对象的构造:

thing: context [
    name: description: none
    new: [name: args/1 description: args/2]
]

derivative: new thing ["Foo" "Bar"]

请注意,这种方法适用于 Rebol 2 和 3。

于 2013-10-07T19:02:26.927 回答
3

实际上,通过再次阅读 Rebol Core 文档(我只是遵循了好的旧建议:“阅读法语手册”),还有另一种实现构造函数的方法,非常简单:

http://www.rebol.com/docs/core-fr/fr-rebolcore-10.html#section-8

当然它也在英文手册中:

http://www.rebol.com/docs/core23/rebolcore-10.html#section-7

=>

另一个使用 self 变量的例子是一个克隆自身的函数:

person: make object! [
    name: days-old: none
    new: func [name' birthday] [
        make self [
            name: name'
            days-old: now/date - birthday
        ]
    ]
]

lulu: person/new "Lulu Ulu" 17-May-1980

print lulu/days-old
7366

我觉得这很方便,这样构造函数就位于对象中。这一事实使对象更加自给自足。

我刚刚成功地为一些地质资料实现了这一点,而且效果很好:

>> source orientation
orientation: make object! [
    matrix: []
    north_reference: "Nm"
    plane_quadrant_dip: ""
    new: func [{Constructor, builds an orientation object! based on a measurement, as given by GeolPDA device, a rotation matrix represented by a suite of 9 values} m][
        make self [
            foreach [a b c] m [append/only matrix to-block reduce [a b c]] 
            a: self/matrix/1/1 
            b: self/matrix/1/2 
            c: self/matrix/1/3 
            d: self/matrix/2/1 
            e: self/matrix/2/2 
            f: self/matrix/2/3 
            g: self/matrix/3/1 
            h: self/matrix/3/2 
            i: self/matrix/3/3 
            plane_normal_vector: reduce [matrix/1/3 
                matrix/2/3 
                matrix/3/3
            ] 
            axis_vector: reduce [self/matrix/1/2 
                self/matrix/2/2 
                self/matrix/3/2
            ] 
            plane_downdip_azimuth: azimuth_vector plane_normal_vector 
            plane_direction: plane_downdip_azimuth - 90 
            if (plane_direction < 0) [plane_direction: plane_direction - 180] 
            plane_dip: arccosine (plane_normal_vector/3) 
            case [
                ((plane_downdip_azimuth > 315) or (plane_downdip_azimuth <= 45)) [plane_quadrant_dip: "N"] 
                ((plane_downdip_azimuth > 45) and (plane_downdip_azimuth <= 135)) [plane_quadrant_dip: "E"] 
                ((plane_downdip_azimuth > 135) and (plane_downdip_azimuth <= 225)) [plane_quadrant_dip: "S"] 
                ((plane_downdip_azimuth > 225) and (plane_downdip_azimuth <= 315)) [plane_quadrant_dip: "W"]
            ] 
            line_azimuth: azimuth_vector axis_vector 
            line_plunge: 90 - (arccosine (axis_vector/3))
        ]
    ]
    repr: func [][
        print rejoin ["Matrix: " tab self/matrix 
            newline 
            "Plane: " tab 
            north_reference to-string to-integer self/plane_direction "/" to-string to-integer self/plane_dip "/" self/plane_quadrant_dip 
            newline 
            "Line: " tab 
            rejoin [north_reference to-string to-integer self/line_azimuth "/" to-string to-integer self/line_plunge]
        ]
    ]
    trace_te: func [diagram [object!]][
        len_queue_t: 0.3 
        tmp: reduce [
            plane_normal_vector/1 / (square-root (((plane_normal_vector/1 ** 2) + (plane_normal_vector/2 ** 2)))) 
            plane_normal_vector/2 / (square-root (((plane_normal_vector/1 ** 2) + (plane_normal_vector/2 ** 2))))
        ] 
        O: [0 0] 
        A: reduce [- tmp/2 
            tmp/1
        ] 
        B: reduce [tmp/2 0 - tmp/1] 
        C: reduce [tmp/1 * len_queue_t 
            tmp/2 * len_queue_t
        ] 
        L: reduce [- axis_vector/1 0 - axis_vector/2] 
        append diagram/plot [pen black] 
        diagram/trace_line A B 
        diagram/trace_line O C 
        diagram/trace_line O L
    ]
]
>> o: orientation/new [0.375471 -0.866153 -0.32985 0.669867 0.499563 -0.549286 0.640547 -0.0147148 0.767778]
>> o/repr
Matrix:     0.375471 -0.866153 -0.32985 0.669867 0.499563 -0.549286 0.640547 -0.0147148 0.767778
Plane:  Nm120/39/S
Line:   Nm299/0

这种方式的另一个优点是“new”方法定义的变量直接属于对象“instance”(我遇到了一些麻烦,使用其他方法,有时不得不提及self/,必须初始化变量或不初始化变量)。

于 2013-10-11T19:50:48.303 回答
3

我试图找出 OO 在 REBOL 中的工作原理。确实是原型。昨天看到这个页面,启发了我下面的经典 OO 模型,没有重复功能:

;---- Generic function for class or instance method invocation ----;
invoke: func [
    obj  [object!]
    fun  [word!]
    args [block!]
][
    fun: bind fun obj/.class
    ;---- Class method names start with a dot and instance method names don't:
    unless "." = first to-string fun [args: join args obj]
    apply get fun args
]

;---- A class definition ----;
THIS-CLASS: context [
    .class: self                           ; the class refers to itself

    ;---- Class method: create new instance ----;
    .new: func [x' [integer!] /local obj] [
        obj: context [x: x' .class: none]  ; this is the object definition
        obj/.class: self/.class            ; the object will refer to the class
                                           ; it belongs to
        return obj
    ]

    ;---- An instance method (last argument must be the instance itself) ----;
    add: func [y obj] [
        return obj/x + y
    ]
]

然后你可以这样做:

;---- First instance, created from its class ----;
this-object: THIS-CLASS/.new 1
print invoke this-object 'add [2]

;---- Second instance, created from from a prototype ----;
that-object: this-object/.class/.new 2
print invoke that-object 'add [4]

;---- Third instance, created from from a prototype in another way ----;
yonder-object: invoke that-object '.new [3]
print invoke yonder-object 'add [6]

;---- Fourth instance, created from from a prototype in a silly way ----;
silly-object: yonder-object/.class/.class/.class/.class/.new 4
print silly-object/.class/add 8 silly-object
print this-object/.class/add 8 silly-object
print THIS-CLASS/add 8 silly-object

(它在 REBOL 2 中工作,并连续打印 3、6、9、12、12、12。)几乎没有任何开销。可能不难找到十几个其他解决方案。这正是真正的问题:有太多方法可以做到这一点。(也许我们最好使用 LoyalScript。)

于 2014-06-18T11:44:57.720 回答