我曾大胆宣称,Rebol中一个长期存在的问题已经“解决”了……“最终收益”的问题。

但是,当然,这样的主张需要一些同行的审查,并且总会有一些学习或改进的新技巧。所以我想在这里粘贴一些代码。即使当我说“已解决”之类的内容时,它并不能完整说明我的意思……也可以批评它。

最好的文档概述了定义返回的含义是在目前关闭的网站上,但副本在Internet存档上。这真的是针对Rebol的读者,冒着将其变成博客的风险,我将链接到另一个答案并说:


Rebol的模型能够实现自定义控件结构取决于定义范围界定。有一个不可见的(尽管可查询的)属性,各种符号被称为“绑定”,并在“定义”时间设置。

这种不可见的绑定将符号连接到其上下文...允许它充当代码并在远程环境中执行而不会引起混淆。例如仅仅因为您正在编写一个包含符号FOO:的例程,并且有人传入了引用FOO的代码块,所以不会混淆哪个是哪个。以这种方式传递代码的能力使一个人可以编写自己的循环结构或做其他事情,这些事情使工具看起来像是在“盒子里”一样。

很好用于变量和大多数功能。但是RETURN是一个棘手的问题,因为只有一个全局函数执行该操作。即使定义作用域正确地在创作上下文和传递的上下文中保留了与该返回值的绑定,但这无济于事,因为此人的意思是“从我这里返回”而不是“从您那里返回”。


下面是一个原始的“ FUNC”函数构造函数-具有完善的实现,因为本机代码现在已合并到Rebol3的Ren / C分支中。它基于这样的思想,即不存在诸如全局RETURN之类的问题……因此,本机的MAKE for FUNCTION!不知道这个概念。由一个或多个生成器来定义所需的收益。

在下面进行了评论。请注意,它使用Ren / C改进,例如如果不运行主体则不设置UNLESS,以“退出” COMPOSOSE:

 func: make function! [[
    {Defines a function with given spec and body, and definitional RETURN}
    spec [block!] {Help string (opt) followed by arg words (opt type, string)}
    body [block!] {The body block of the function}

    ; Ren/C's MAKE FUNCTION! uses SET-WORD to indicate "pure locals"
    no-return:
][
    make function! compose/deep [
        [
            ; SPEC

            ; We need to figure out two things:
            ;
            ; 1. Is the function <no-return> (e.g. no definitional return
            ;    function definition or CATCH needed in the body?)
            ;
            ; 2. If the function is *not* <no-return>, do we need to add
            ;    a RETURN: "true local" to the spec or is it already there?
            ;
            ; Since our COMPOSE/DEEP is splicing series, we will need two
            ; splices to build the new spec.  One splice is of the passed
            ; in spec (always) and the second for a RETURN: (...maybe)

            (
                no-return: any [

                    ; One way of having no return is if there is a literal
                    ; <no-return> tag in the spec being passed in.  This
                    ; tag is FUNC-specific... all MAKE FUNCTION!s are
                    ; "no return" and it would error if we passed in the
                    ; tag.  However, we can't just take it out because we
                    ; haven't copied the passed-in spec.  We only pay for
                    ; that copy in the event that transparent is found.
                    ; Note that <no-return> is expected to be very rare,
                    ; though useful when used (needed for COLLECT, USE)

                    ; UPDATE: The MAKE FUNCTION! generator has been updated to
                    ; tolerate ANY-STRING! being left residual in the spec, as
                    ; STRING! was tolerated before.  That was motivated by
                    ; realizing this could just say `find spec <no-return>
                    ; ...but it's the generator's choice to remove or not

                    all [
                        no-return: find spec <no-return>
                        spec: copy spec
                        remove at spec index-of no-return
                    ]

                    ; Another way of being told to be transparent is if there
                    ; is a RETURN, /RETURN, :RETURN or other word that is
                    ; *not* a set-word!.  The reason for exempting RETURN:
                    ; and going ahead anyway is that there's little harm in
                    ; having a preloaded value for a "true local" as it is
                    ; not overwriting any passed-in quantity... and it can
                    ; even serve as documentation and compatibility with Red
                    ; (when used under a certain idiom).
                    ;
                    ; We get our no-return decision and leave the spec
                    ; at the position of the set word.

                    all [
                        spec: any [
                            find spec 'return
                            tail spec ;-- let tail be fail vs. none...
                        ]
                        not tail? spec ;-- ...so still have spec intact!
                        not set-word? spec/1
                    ]
                ]

                ; We want the spec left at the position of a possible RETURN
                ; if that's what it is be positioned on a possible RETURN:, so
                ; we splice from the head position, yet leave spec where it is

                head spec
            )

            ; Now we have two reasons to not add the RETURN: set-word... if
            ; it's already there, or if we are transparent...

            (unless any [
                no-return
                set-word? spec/1
            ][
                quote return:
            ])
        ] (
            ; BODY

            ; Now for the body.  Compared to doing the spec analysis work,
            ; it's a lot easier to understand.  We either want just the
            ; plain body if we were transparent, or to pair up a
            ; definitional return with a named throw + catch if not.
            ;
            ; Named THROW/CATCH is fairly well understood at this point, but
            ; the interesting bit is that if we are definitional then the
            ; word RETURN (that we know is local) has its binding retrieved.
            ; That binding is the name of the throw and catch, and in the
            ; case of a function it will be the FUNCTION! itself (new
            ; feature, previously it returned TRUE for function locals).
            ; For CLOSURE! it will be the unique object associated with
            ; that invocation...as before.

            either no-return compose/deep [[[(body)]]] compose/deep [[[
                return: make function! [
                    ["Returns a value from a function." value [any-value!]]
                    [throw/name :value bind-of 'return]
                ]
                catch/name [
                    (body)
                ] bind-of 'return
            ]]]
        )
    ]
]]
 


上述内容的快速快照……当然,使用名称RETURN没什么特别的。您可以随心所欲地调用它,给它任何喜欢的东西,以不同的方式组合零件...这是解决“类似返回问题”的一种模式:-)

 >> foo: func [x] [
    print "yes"
    return (10 + x)
    print "no"
]
== make function! [[x return:][
    return: make function! [
        ["Returns a value from a function." value [any-value!]]
        [throw/name :value bind-of 'return]]
    catch/name [
        print "yes"
        return (10 + x)
        print "no"
    ] bind-of 'return
]]

>> foo 20
yes
== 30
 


...另一个示例...

 >> bar: func [] [
    help return
    print [newline "It even has help. :-)"]
]
== make function! [[return:][
    return: make function! [
        ["Returns a value from a function." value [any-value!]] 
        [throw/name :value bind-of 'return]]
    catch/name [
        help return
        print [newline "It even has help. :-)"]
    ] bind-of 'return
]]

>> bar
USAGE:
        RETURN value

DESCRIPTION:
        Returns a value from a function.
        RETURN is a function value.

ARGUMENTS:
        value (any-value!)

It even has help. :-)
 


这是生成器的纯用户模式变体。要说定义性返回是“已解决”,是指原理图与上述代码的纯本机模拟的组合,并且无需在语言中构建关键字(例如RETURN),这会违背“ no内置关键字”目标”。

以下是有关其撰写时的相对速度的非常初步的数据点:

 ; Mezzanine definitional FUNC (exactly as above)
>> delta-time [loop 1000 [foo: func-mezz [a] [return a * 2] loop 1000 [foo 5]]]
== 0:00:03.46531

; Native definitional FUNC (simulates effects w/o boilerplate)
>> delta-time [loop 1000 [foo: func-native [a] [return a * 2] loop 1000 [foo 5]]]
== 0:00:00.800268
 


后一个数字有很多优化方法,但上面的第一个可能有一些想法可以提出...


更新-如果所有评论都是为了清楚阅读或提供调整,以下是一个没有评论的版本:

 func: make function! [[spec [block!] body [block!] no-return:][
    make function! compose/deep [
        [
            (
                transparent: any [
                    all [
                        no-return: find spec <no-return>
                        spec: copy spec
                        remove at spec index-of no-return
                    ]
                    all [
                        spec: any [
                            find spec 'return
                            tail spec
                        ]
                        not tail? spec
                        not set-word? spec/1
                    ]
                ]
                head spec
            )
            (unless any [no-return set-word? spec/1][quote return:])
        ] (
            either no-return compose/deep [[[(body)]]] compose/deep [[[
                return: make function! [
                    [value [any-value!]]
                    [throw/name :value bind-of 'return]
                ]
                catch/name [(body)] bind-of 'return
            ]]]
        )
    ]
]]
 


评论

(注意:如果CodeReview的读者以前从未见过这种灵活性,并且想了解更多,请参阅Rebol和Red chat ...)

这个问题已经存在很长时间了……顺便说一句,我确实认为//或;;可能是评论...