smalltalk - 为什么我不应该在 Smalltalk 中存储到文字数组中?

标签 smalltalk pharo squeak

一些风格指南和习语建议你不应该改变文字数组,比如在这种情况下:

MyClass>>incrementedNumbers

    | numbers |
    numbers := #( 1 2 3 4 5 6 7 8 ).
    1 to: numbers size: do: [:index |
        numbers at: index put: (numbers at: index) + 1].
    ^ numbers

为什么我不应该这样做?

最佳答案

注意:以下内容取决于实现。 ANSI Smalltalk Standard定义:

It is unspecified whether the values of identical literals are the same or distinct objects. It is also unspecified whether the values of separate evaluations of a particular literal are the same or distinct objects.



也就是说,您不能依赖两个(相等的)文字相同或不同。但是,以下是常见的实现

Squeak 和 Pharo 中的文字数组

至少在 Squeak 和 Pharo 中,文字数组是在保存(=编译)方法时构造的,并存储在方法对象中(a CompiledMethod)。这意味着更改文字数组会更改存储在方法对象中的值。例如:
MyClass>>example1

    | literalArray |
    literalArray := #( true ).
    literalArray first ifTrue: [
       literalArray at: 1 put: false.
       ^ 1].
    ^ 2

此方法返回 1仅在第一次调用时:
| o p |
o := MyClass new.
o example1. "==> 1"
o example1. "==> 2"
o example1. "==> 2"
p := MyClass new.
p example1. "==> 2"

这甚至与接收器无关。

但是,你不能依赖那个 ,在其他 Smalltalks 中可能会有所不同。

不同的方法
  • 复制(始终安全)
    为了克服这个问题,您可以在使用前简单地复制文字数组。你的例子:
    MyClass>>incrementedNumbers
    
        | numbers |
        numbers := #( 1 2 3 4 5 6 7 8 ) copy. "<====== "
        1 to: numbers size: do: [:index |
            numbers at: index put: (numbers at: index) + 1].
        ^ numbers
    

    这始终是安全的,并且不会改变方法对象中的数组。
  • 支撑阵列(主要是可移植的)
    虽然标准中没有定义,但大多数实现都支持这样的花括号数组表达式:
    { 1 . 'foo' . 2 + 3 }. 
    

    这相当于:
    Array with: 1 with: 'foo' with: 2 + 3.
    

    这些数组是在执行时构造的(与文字数组相反),因此可以安全使用。你的例子再次:
    MyClass>>incrementedNumbers
    
        | numbers |
        numbers := { 1 . 2 . 3 . 4 . 5 . 6 . 7 . 8 }. "<====== "
        1 to: numbers size: do: [:index |
            numbers at: index put: (numbers at: index) + 1].
        ^ numbers
    

  • (Ab) 使用文字数组

    有时确实有理由改变文字数组(或者更普遍的任何方法文字,坦率地说)。例如,如果您有静态信息,如根本不更改但并不总是使用的图像或二进制数据,但您不能(无论出于何种原因)使用实例或类变量,您可以将对象存储在文字数组中首次使用时:
    MyClass>>staticInformation
    
        | holder |
        holder := #( nil ).
        holder first ifNil: [ holder at: 1 put: self generateBinaryData ].
        ^ holder first
    
    ifNil: check 只会在第一次执行该方法时为真,后续执行只会返回 self generateBinaryData 返回的值。在第一次调用期间。

    这种模式被一些框架使用了一段时间。
    然而,特别是对于二进制数据,大多数 Smalltalks(包括 Squeak 和 Pharo)现在支持形式为 #[ … ] 的文字字节数组。 .该方法可以简单地写为
    MyClass>>staticInformation
    
        ^ #[42 22 4 33 4 33 11 4 33 0 0 0 0 
            4 33 18 4 33 4 33 9 0 14 4 33 4 
            33 7 4 33 0 0 9 0 7 0 0 4 33 10
            4 33 4 33 7 4 33 0 0 9 0 7 0 0 4 
            " ... "
            33 10 4 33 4 33 17 0 11 0 0 4 33
            4 33 0 0 17 0 7 0 0 4 33 13 0]
    

    关于smalltalk - 为什么我不应该在 Smalltalk 中存储到文字数组中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29964345/

    相关文章:

    oop - 为什么 Smalltalk 支持一流的功能,而不支持其他功能特性?

    repository - 在不指定包的情况下将 repo 添加到 Pharo smalltalk

    smalltalk:关于方法 - "withArgs:executeMethod:"

    smalltalk - 如何在不使用步骤的情况下为变形制作动画?

    oop - 闲聊中的矛盾

    smalltalk - 具有完美功能集的 Smalltalk 实现

    python - Smalltalk、Perl、Python 和 Ruby 之间的集合和流类等价物

    oop - 使用断言 :equals: in pharo

    smalltalk - Squeak/Pharo/Newspeak Smalltalk 虚拟机有什么区别?

    pharo - 更改悬停时出现在弹出窗口中的边缘名称