gpt4 book ai didi

function - 为什么在 REBOL 中函数 "have memory"?

转载 作者:行者123 更新时间:2023-12-04 13:59:58 25 4
gpt4 key购买 nike

在 rebol 中,我编写了这个非常简单的函数:

make-password: func[Length] [
chars: "QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
password: ""
loop Length [append password (pick chars random Length)]
password
]

当我连续多次运行时,事情变得非常困惑:
loop 5 [print make-password 5]

给出(例如)这个输出:
  • TWTQW
  • TWTQWWEWRT
  • TWTQWWEWRTQWWTW
  • TWTQWWEWRTQWWTWQTTQQ
  • TWTQWWEWRTQWWTWQTTQQTRRTT

  • 看起来该函数记住了过去的执行并存储了结果,然后再次使用它!

    我没有问这个!

    我希望输出类似于以下内容:
  • IPS30
  • DQ6BE
  • E70IH
  • XGHBR
  • 7LMN5

  • 我怎样才能达到这个结果?

    最佳答案

    一个好问题。

    Rebol 代码实际上最好被认为是一种非常程式化的数据结构。该数据结构“恰好是可执行的”。但是你需要了解它是如何工作的。

    例如,来自@WiseGenius 的建议:

    make-password: func[Length] [
    chars: "QWERTYUIOPASDFGHJKLZXCVBNM1234567890"
    password: copy ""
    loop Length [append password (pick chars random Length)]
    password
    ]

    查看包含 append password... 的块。该块在那里“成像”;引擎盖下的真实样子是:
    chars: **pointer to string! 0xSSSSSSS1**
    password: copy **pointer to string! 0xSSSSSSS2**
    loop Length **pointer to block! 0xBBBBBBBB**
    password

    当解释器加载它们时,所有系列都以这种方式工作。字符串、块、二进制文件、路径、括号等。 鉴于它是“一路向下的海龟”,如果你一直到那个指针,块 0xBBBBBBBB 是内部的:
    append password **pointer to paren! 0xPPPPPPPP**

    这样做的一个结果是一个系列可以在多个地方被引用(并因此被“成像”):
    >> inner: [a]

    >> outer: reduce [inner inner]
    [[a] [a]]

    >> append inner 'b

    >> probe outer
    [[a b] [a b]]

    这可能会让新手感到困惑,但是一旦您了解了数据结构,您就会开始知道何时使用 COPY。

    所以你已经注意到函数的一个有趣的含义。考虑这个程序:
    foo: func [] [
    data: []
    append data 'something
    ]

    source foo

    foo
    foo

    source foo

    这产生了一个可能令人惊讶的结果:
    foo: func [][
    data: []
    append data 'something
    ]

    foo: func [][
    data: [something something]
    append data 'something
    ]

    我们多次调用 foo ,看起来函数的源代码在我们这样做时正在改变。从某种意义上说,它是自修改代码。

    如果这让您感到困扰,R3-Alpha 中有一些工具可以攻击它。您可以使用 PROTECT 来保护函数体免遭修改,甚至可以创建您自己的替代例程,例如 FUNC 和 FUNCTION 将为您执行此操作。 (PFUNC?PFUNCTION?)在 Rebol 版本 3 中,您可以编写:
    pfunc: func [spec [block!] body [block!]] [
    make function! protect/deep copy/deep reduce [spec body]
    ]

    foo: pfunc [] [
    data: []
    append data 'something
    ]

    foo

    当你运行时,你会得到:
    *** ERROR
    ** Script error: protected value or series - cannot modify
    ** Where: append foo try do either either either -apply-
    ** Near: append data 'something

    所以这迫使你复制系列。它还指出FUNC只是一个函数!本身,FUNCTION 也是。您可以制作自己的发电机。

    这可能会打断您的大脑,您可能会尖叫着说“这不是编写软件的任何明智方式”。或者你可能会说“我的天哪,满是星星”。 react 可能会有所不同。但它是为系统提供动力并赋予其极大灵活性的“技巧”的基础。

    (注意: Ren-C branch of Rebol3 从根本上做到了,函数体——以及一般的源代码系列——在默认情况下是锁定的。如果你想要一个函数中的静态变量,你可以说 foo: func [x <static> accum (copy "")] [append accum x | return accum] 并且该函数将在 accum 跨调用。)

    我还建议密切关注每次运行中实际发生的情况。在运行 foo 函数之前,数据没有值(value)。每次我们执行函数并且评估器看到一个 SET-WORD 时会发生什么!后跟一个系列值,它执行对变量的赋值。
    data: **pointer to block! 0xBBBBBBBB**

    在分配之后,您将有两个对现有块的引用。一个是它存在于在 LOAD 时建立的代码结构中,在函数运行之前。第二个引用是存储在数据变量中的引用。正是通过这第二个引用,您正在修改这个系列。

    请注意,每次运行该函数时都会重新分配数据。但是一遍又一遍地重新分配给相同的值......原来的块指针!这就是为什么如果你想在每次运行时都有一个新的块,你必须 COPY。

    掌握评估器规则中潜在的简单性是令人头晕目眩的有趣性的一部分。这就是将简单性打扮成一种语言的方式(在某种程度上,您可以根据自己的方式进行调整)。例如,没有“多重赋值”:
    a: b: c: 10

    这只是评估器将 a: 作为 SET-WORD!符号并说“好吧,让我们将其绑定(bind)上下文中的变量 与下一个完整表达式产生的任何内容相关联。”。 b: 也一样。 c: 做同样的事情,但由于整数值 10 而命中终端......然后也计算为 10。所以它看起来像多重赋值。

    所以请记住,系列文字的原始实例是卡在加载的源中的那个。如果评估者有时间做这种 SET-WORD!或 SET 赋值,它将借用指向源中该文字的指针来插入变量。这是一个可变的引用。您(或您设计的抽象)可以使用 PROTECT 或 PROTECT/DEEP 使其不可变,并且您可以使用 COPY 或 COPY/DEEP 使其非引用。

    相关说明

    有些人认为你永远不应该写 copy [] ...因为(a)你可能会养成忘记写 COPY 的习惯,并且(b)你每次做的时候都在制作一个未使用的系列。那个“空白系列模板”被分配,必须被垃圾收集器扫描,并且没有人真正接触过它。

    如果你写 make block! 10 (或您想要预分配块的任何大小),您可以避免该问题,保存系列并提供大小调整提示。

    关于function - 为什么在 REBOL 中函数 "have memory"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25935648/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com