【问题标题】:is there an object constructor in rebolrebol 中是否有对象构造函数
【发布时间】:2013-10-14 06:56:42
【问题描述】:

我通常以“本能”的方式通过函数进行编程,但我现在的问题可以很容易地通过对象来解决,所以我继续使用这种方法。

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

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

【问题讨论】:

    标签: oop constructor rebol rebol2


    【解决方案1】:

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

    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
    ]
    

    您还可以编写一个更通用的 new 函数,该函数将基对象作为第一个参数,并在克隆对象后自动调用构造函数,但支持可选构造函数参数那么通用的方法就比较棘手了。

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

    【讨论】:

    • 是的,你们最终都得到了非常相似的解决方案。初始化还是新的?这就是问题所在,给构造方法命名……当然,如果要生成大量的对象,这种方法绝对应该避免。在我目前的问题中,我实际上是在考虑几个对象,甚至不是十几个。
    【解决方案2】:

    据我所知,没有正式的方法/约定可以使用 init() 等对象构造函数。当然还有内置的构造派生对象的方法:

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

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

    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。

    【讨论】:

    • 好的:那么,如果需要,我们所需要的只是对象中构造函数方法的适当约定。
    【解决方案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/,必须初始化变量或不是)。

    【讨论】:

      【解决方案4】:

      我正在尝试了解 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。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-01-13
        • 1970-01-01
        • 2021-07-16
        • 1970-01-01
        • 2017-09-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多