rebol - Red/Rebol 函数如何通过值或引用传递参数?

标签 rebol red

我对以下两个代码的结果感到困惑:

代码1:

>> f: func [x][head insert x 1]
== func [x][head insert x 1]
>> a: [2 3]
== [2 3]
>> f a
== [1 2 3]
>> a
== [1 2 3] ;; variable a is destroyed

代码2:

>> g: func [x y][x: x + y]
== func [x y][x: x + y]
>> c: 1 d: 2
== 2
>> g c d
== 3
>> c
== 1
>> d
== 2
;;variable c and d keep their original values

我的问题是:Red/Rebol 中的函数如何通过值或引用获取其参数?

最佳答案

这是个好问题。答案是:参数是按值传递的,但有些参数中可能包含引用。

Rebol/Red 中的每个值都表示为统一大小的盒装结构,通常称为单元。该结构在逻辑上分为 3 个部分: header、payload 和 extra。

  ┌─────────┐
  │ header  │
  ├─────────┤ Historically, the size of the value slot is 4 machine pointers in size,
  │ payload │ 1 for header, 1 for extra, and 2 for payload.
  │         │ E.g. on 32-bit systems that's 128 bits, or 16 bytes.
  ├─────────┤ 
  │ extra   │
  └─────────┘
  • header 包含各种元信息,有助于识别有效负载包含的值,其中最重要的部分是类型标记,或者用 Rebol 的说法,是数据类型 ID
  • 有效负载包含某些值的数据表示形式,例如数字、字符串、字符等。
  • 额外部分用作为优化(例如缓存)和存储不适合负载的数据而保留的空间。

现在,值槽具有统一的大小,自然地,某些数据根本无法完全放入其中。为了解决这个问题,值槽可以包含对外部缓冲区的引用(基本上是一个带有额外间接层的指针,以使数据可垃圾收集并在多个槽之间共享)。

  ┌─────────┐
  │ header  │
  ├─────────┤
  │ payload │ --→ [buffer]
  │         │
  ├─────────┤
  │ extra   │
  └─────────┘

适合值槽的值(例如 scalar! )称为直接,不适合值槽的值(例如 series! )称为间接>:因为其中的引用在值槽和实际数据之间引入了一定程度的间接性。例如,here这就是 Red 中定义各种插槽布局的方式。

值槽的内容只是一堆字节;运行时如何解释它们取决于 header 中的数据类型 ID。某些字节可能只是文字,而其他字节可能是指向数据缓冲区的间接指针。将参数传递给函数只是复制这些字节,无论它们的含义如何。因此,在这方面,文字和引用的处理方式相同。

因此,如果您有一个内部看起来像这样的值槽:

┌────────┐
│DEADBEEF│ header
├────────┤
│00000000│ payload
│FACEFEED│
├────────┤
│CAFEBABE│ extra
└────────┘

那么,FACEFEED可以是有符号整数-87097619 ,或打包在一起的不同大小的位字段,或者它可以是机器指针:这取决于 header 中的数据类型 ID(例如 EF 字节)归属于它。

当值槽作为参数传递给函数时,其所有字节都将简单地复制到计算堆栈上,无论它们编码或表示什么。对于直接值,逻辑很简单:如果在函数内修改参数,则原始值保持不变,因为它只是一个副本。这就是您的第二个示例的全部内容。

 Parameter        Stack
┌────────┐      ┌────────┐
│DEADBEEF│      │DEADBEEF│
├────────┤      ├────────┤
│00000000│      │00000000│ Both represent the same integer -87097619.
│FACEFEED│      │FACEFEED│ ← You modify this one, with no effect on the other.
├────────┤      ├────────┤
│CAFEBABE│      │CAFEBABE│
└────────┘      └────────┘

但是使用间接值会更有趣。它们也被逐字复制,但这使得两个副本共享对单个缓冲区的相同引用(请记住,表示引用的字节在两个槽中是相同的)。因此,当您通过一个缓冲区(例如头部的 insert 元素)修改缓冲区时,另一个缓冲区也会反射(reflect)更改。

 Parameter        Stack
┌────────┐      ┌────────┐
│DEADBEEF│      │DEADBEEF│
├────────┤      ├────────┤
│00000000│      │00000000│ Both refer to the same buffer (same machine pointers!)
│FACEFEED│──┐───│FACEFEED│ 
├────────┤  │   ├────────┤
│CAFEBABE│  │   │CAFEBABE│
└────────┘  │   └────────┘
            ↓
      [b u f f e r] ← You modify the content of the buffer.

回到第一个例子:

>> f: func [x][head insert x 1]
== func [x][head insert x 1]
>> a: [2 3]
== [2 3]
>> f a
== [1 2 3]
>> a
== [1 2 3] ;; variable a is destroyed

简化了很多,这就是它在引擎盖下的样子:

       value slot               buffer     value slot (parameter on stack)
<word a in global context> --→ [1 2 3] ←-- <word x in function's context>

当然,有多种方法可以克隆值槽它所引用的缓冲区:这就是 copy确实如此。

>> f: func [x][head insert x 1]
== func [x][head insert x 1]
>> a: [2 3]
== [2 3]
>> f copy a
== [1 2 3]
>> a
== [2 3]

用图解法(再次简化一下):

value slot          buffer
     <x>     --→   [1 2 3]
     <a>     --→   [2 3]

系列值(例如 block )的有效负载中还包含另一部分数据:索引。

>> at [a b c d] 3 ; index 3, buffer → [a b c d]
== [c d]

当将 block 作为参数传递时,它的索引也会被复制,但与数据缓冲区不同的是,它不在两个值槽之间共享。

 Parameter        Stack
┌────────┐      ┌────────┐
│DEADBEEF│      │DEADBEEF│
├────────┤      ├────────┤
│00000000│      │00000000│ Suppose that 0's here are an index.
│FACEFEED│──┐───│FACEFEED│ Modifying this one won't affect the other.
├────────┤  │   ├────────┤
│CAFEBABE│  │   │CAFEBABE│
└────────┘  │   └────────┘
            ↓
      [b u f f e r]

所以:

>> foo: func [x][x: tail x] ; tail puts index, well, at the tail
== func [x][x: tail x]
>> y: [a b c]
== [a b c]
>> foo y
== [] ; x is modified
>> y
== [a b c] ; y stays the same

关于rebol - Red/Rebol 函数如何通过值或引用传递参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66793343/

相关文章:

html - 使用 REBOL 在文件中查找/替换跨行的字符串

sorting - 如何对红色列表进行反向排序?

compilation - 在 Red 编程语言中,如何编译脚本?

rebol - 日期减法为什么我得不到hh :mm:ss in Rebol/Red?

rebol - 为什么 rebol 使用 https 返回错误

networking - 如何处理 Rebol 3 方案中的超时时间

rebol - 将单词更改为路径

parsing - rebol解析问题

parsing - 红色的递归解析

rebol - 了解红 block 评估