一尘不染

字符串不可变的非技术优势

java

我想知道从程序员的角度来看字符串类型不可变的好处。

另外,在可变的字符串类型中,要么您已经内置了线程安全性(然后又很难进行优化),要么您必须自己进行。无论如何,您都可以选择使用具有内置线程安全性的可变字符串类型,因此这并不是可变字符串类型的真正优势。(同样,更容易进行处理和优化以确保不可变类型上的线程安全,但这不是重点。)

但是,不可变字符串类型在用法中有什么好处?使某些类型不可变而其他类型不可变又有什么意义呢?这对我来说似乎很不一致。

在C ++中,如果我想使某些字符串不可变,则将其作为对函数(const std::string&)的const引用传递。如果我想要原始字符串的可变副本,请将其作为传递std::string。仅当我想使其可变时,才将其作为参考(std::string&)传递。因此,我可以选择要执行的操作。我可以使用所有可能的类型来做到这一点。

在Python或Java中,某些类型是不可变的(大多数都是原始类型和字符串),而其他则不是。

在像Haskell这样的纯函数式语言中,一切都是不可变的。

有充分的理由使这种不一致有意义吗?还是纯粹出于技术上的低级原因?


阅读 217

收藏
2020-12-03

共1个答案

一尘不染

使某些类型不可变而其他类型不可变又有什么意义呢?

如果没有 一些 可变的类型,您将不得不全力以赴地进行纯函数式编程-
与目前最流行的OOP和过程方法完全不同的范例,并且尽管功能非常强大,但对许多程序员来说显然非常具有挑战性(当您 确实
需要一种无法改变的语言带来的副作用时,会发生什么?当然,在现实世界中编程不可避免地会做这是挑战的一部分-
Haskell的Monads是一种非常优雅的方法,例如,但是程序员是否知道您完全自信地理解它们,并且可以将其与典型的OOP构造一起使用?-)。

如果您不了解拥有多个范式的巨大价值(FP范式 关键依赖可变数据的范式),我建议您学习Haridi和Van
Roy的杰作《计算机编程的概念,技术和模型 -“
SICP正如我曾经描述的那样;-)。

大多数程序员,无论是否熟悉Haridi和Van Roy,都会轻易地承认拥有至少 一些
可变数据类型对他们很重要。尽管我从您的Q中引用了上面的句子,这句话有完全不同的观点,但我认为这可能也是您困惑的根源: 不是
“为什么每个都有些”,而是“为什么有些 不可变 的”。

在Fortran实现中曾经(偶然地)获得了“彻底可变”的方法。如果有的话

  SUBROUTINE ZAP(I)
  I = 0
  RETURN

然后是一个程序段,例如

  PRINT 23
  ZAP(23)
  PRINT 23

会打印23,然后是0- 数字23
已经被突变,因此程序其余部分中对23的所有引用实际上都将引用0。从技术上来说,这不是编译器中的错误:Fortran对于您的程序有微妙的规则在将常量vs变量传递给分配给其参数的过程时,是并且不允许这样做,并且此代码段违反了那些鲜为人知,不可编译的规则,因此这是程序中的一个问题,而不是编译器中的问题。当然,实际上,以这种方式引起的错误数量很高,因此,典型的编译器很快会在这种情况下切换到破坏性较小的行为(如果操作系统支持,则将只读段中的常量获取运行时错误;或者,传递新
副本 尽管有额外开销,但常量的大小而不是常量本身;等等),尽管从技术上讲它们是程序错误,但允许编译器“非常正确地”显示未定义的行为;-)。

在某些其他语言中强制执行的替代方法是增加参数传递的多种方式的复杂性-最值得注意的可能是在C
++中,它带有按值,按引用,按常量引用,按指针,按常量指针等等。然后当然,您会看到程序员对这样的声明感到困惑(例如,const foo* const bar最右边const的基本上无关紧要,如果它bar是某个函数的参数…而关键bar是如果它是 局部变量 …!-)。

实际上,Algol-68可能沿着这个方向走得更远(如果您可以有一个值和一个参考,为什么不参考一个参考呢?还是参考这个参考呢?&c-Algol
68对此没有限制,并且规则定义正在发生的事情可能是“打算用于实际用途”编程语言中发现的最微妙,最困难的组合)。早期的C语言(仅具有按值和明确的指针-不const,没有参考,没有并发症)无疑是对它的部分反应,就像最初的Pascal一样。但是const很快就悄悄涌入,复杂性又开始增加。

Java和Python(以及其他语言)以强大的简化方式切入了这个丛林:所有参数传递
所有赋值都是“按对象引用”(从不引用变量或其他引用,从不包含语义隐式副本,&c)
。将数字定义为(至少)语义上不可变的,可以避免上面的Fortran代码所展示的“麻烦”,从而保留了程序员的理智(以及语言简单性的这一宝贵方面)。

Treating strings as primitives just like numbers is quite consistent with the
languages’ intended high semantic level, because in real life we do need
strings that are just as simple to use as numbers; alternatives such as
defining strings as lists of characters (Haskell) or as arrays of characters
(C) poses challenges to both the compiler (keeping efficient performance under
such semantics) and the programmer (effectively ignoring this arbitrary
structuring to enable use of strings as simple primitives, as real life
programming often requires).

Python通过添加一个简单的不可变容器(tuple)并将 散列
与“有效不可变性”联系在一起(这避免了对程序员的某些惊奇,例如在Perl中发现,其哈希允许可变字符串作为键),从而使Python更进一步。为什么不?一旦有了不变性(这是一个宝贵的概念,可以使程序员不必学习用于赋值和参数传递的N种不同的语义,随着时间的流逝,N会逐渐增加;-),您也可能会从中受益匪浅;-)

2020-12-03