对不起,我的标题太含糊了——如果我能想出一个简洁的标题,我就不用问这个问题了。

假设我有一个不可变的列表类型。它有一个操作Foo(x),该操作返回一个新的不可变列表,其中指定的参数作为结尾的额外元素。因此,要建立一个包含"Hello", "immutable", "world"值的字符串列表,你可以这样写:

var empty = new ImmutableList<string>();
var list1 = empty.Foo("Hello");
var list2 = list1.Foo("immutable");
var list3 = list2.Foo("word");

(这是c#代码,我最感兴趣的是c#建议,如果你觉得语言很重要的话。这根本不是一个语言问题,但语言中的习语可能很重要。)

重要的是现有的列表不会被Foo改变,所以是空的。Count仍然返回0。

另一种(更习惯的)达到最终结果的方式是:

var list = new ImmutableList<string>().Foo("Hello")
                                      .Foo("immutable")
                                      .Foo("word");

我的问题是:Foo最好的名字是什么?

编辑3:正如我稍后所揭示的,类型的名称实际上可能不是ImmutableList<T>,这使得位置很清楚。相反,想象它是TestSuite,它是不可变的,因为它所在的整个框架是不可变的……

(编辑3结束)

到目前为止,我想到的选项是:

Add: common in .NET, but implies mutation of the original list Cons: I believe this is the normal name in functional languages, but meaningless to those without experience in such languages Plus: my favourite so far, it doesn't imply mutation to me. Apparently this is also used in Haskell but with slightly different expectations (a Haskell programmer might expect it to add two lists together rather than adding a single value to the other list). With: consistent with some other immutable conventions, but doesn't have quite the same "additionness" to it IMO. And: not very descriptive. Operator overload for + : I really don't like this much; I generally think operators should only be applied to lower level types. I'm willing to be persuaded though!

我选择的标准是:

给出方法调用结果的正确印象(即它是带有额外元素的原始列表) 尽可能清楚地表明它不会改变现有的列表 当像上面的第二个例子一样连接在一起时,听起来很合理

如果我说得不够清楚,请再问我一些细节。

编辑1:以下是我更喜欢Plus而不是Add的理由。考虑以下两行代码:

list.Add(foo);
list.Plus(foo);

在我看来(这是我个人的观点),后者显然是有问题的——它就像写“x + 5;”作为一个单独的语句。第一行看起来是可以的,直到您记得它是不可变的。事实上,加号操作符本身不改变操作数的方式是我最喜欢加号的另一个原因。没有操作符重载的麻烦,它仍然具有相同的含义,其中包括(对我来说)不改变操作数(在本例中是方法目标)。

编辑2:不喜欢添加的原因。

各种各样的回答都很有效:“使用Add。这就是DateTime所做的,而String有Replace方法等,这些方法不会使不可变性变得明显。”我同意,这里有优先权。然而,我见过很多人称之为DateTime。添加或字符串。替换并期待突变。有大量的新闻组问题(如果我仔细研究的话,可能会有这样的问题),这些问题的答案是“您忽略了String.Replace的返回值;字符串是不可变的,返回一个新的字符串"

现在,我应该揭示这个问题的一个微妙之处——类型实际上可能不是一个不可变列表,而是一个不同的不可变类型。特别是,我正在开发一个基准测试框架,在该框架中,您可以向套件中添加测试,并创建一个新的套件。这可能是显而易见的:

var list = new ImmutableList<string>();
list.Add("foo");

不会完成任何事情,但当你把它改成:

var suite = new TestSuite<string, int>();
suite.Add(x => x.Length);

看起来应该没问题。然而,对我来说,这个错误更清楚了:

var suite = new TestSuite<string, int>();
suite.Plus(x => x.Length);

这只是在乞求:

var suite = new TestSuite<string, int>().Plus(x => x.Length);

理想情况下,我希望我的用户不必被告知测试套件是不可变的。我想让他们坠入成功的深渊。这可能是不可能的,但我想试试。

很抱歉,我只讨论了不可变的列表类型,过度简化了最初的问题。并非所有的集合都像ImmutableList<T>:)那样具有自描述性。


我最终选择在BclExtras中添加我所有的不可变集合。原因是,这是一个容易预测的名字。我不太担心人们会混淆Add和一个变化的Add,因为类型名的前缀是Immutable。

有一段时间,我考虑了Cons和其他函数样式名称。最后我对他们不屑一顾,因为他们没有那么出名。函数式程序员当然能理解,但他们不是大多数用户。

其他名字:你提到过:

另外:我在这件事上犹豫不决。对我来说,这并不像Add那样是一个非变异操作 使用:会导致VB出现问题(双关语) 操作符重载:可发现性将是一个问题

我考虑的选择:

Concat:字符串是不可变的,使用这个。不幸的是,它只适合加在后面 CopyAdd:复制什么?线人,名单? AddToNewList:也许是一个很好的列表。但是集合、堆栈、队列等等呢?

不幸的是,似乎真的没有一个词是这样的

绝对是一个不可变的操作 大多数用户都能理解 少于4个词即可表达

当您考虑List之外的集合时,情况就更加奇怪了。以Stack为例。即使是第一年的程序员也会告诉你堆栈有Push/Pop方法对。如果你创建了一个ImmutableStack,并给它一个完全不同的名字,让我们叫它Foo/Fop,你只是增加了更多的工作,让他们使用你的集合。

编辑:对+编辑的响应

我明白你用Plus的意思了。我认为更有说服力的情况应该是减号。如果我看到下面的内容,我肯定会想知道程序员到底在想什么

list.Minus(obj);

我对正负搭配或新搭配最大的问题是感觉太过了。集合本身已经有了一个可区分的名称,即Immutable前缀。为什么要进一步添加词汇表,其目的是添加与Immutable前缀已经做的相同的区别呢?

我可以看到call site参数。从单个表达式的角度来看,它更清楚。但在整个函数的上下文中,这似乎是不必要的。

编辑2

我同意人们肯定被String弄糊涂了。Concat和DateTime.Add。我见过几个非常聪明的程序员遇到过这个问题。

然而,我认为ImmutableList是一个不同的参数。对于程序员来说,String或DateTime没有将其建立为不可变的。你必须知道它通过其他来源是不可变的。因此,这种困惑并非出乎意料。

ImmutableList没有这个问题,因为名称定义了它的行为。你可能会说人们不知道什么是不可变的,我认为这也是有道理的。我当然是在大学二年级才知道的。但是无论您选择什么名称而不是Add,都有同样的问题。

编辑3:像TestSuite这样不可变但不包含单词的类型呢?

我认为这说明你不应该发明新的方法名。也就是说,为了便于并行操作,显然有一种使类型不可变的驱动力。如果您专注于更改集合的方法名称,那么下一步将是在您使用的每个不可变类型上更改方法名称。

我认为更有价值的做法是将类型标识为Immutable。这样,您就可以解决问题,而不必重新考虑每个突变的方法模式。

现在,您如何将TestSuite识别为不可变的呢?在今天的环境下,我认为有几种方法

前缀Immutable: ImmutableTestSuite 添加一个描述不可变级别的属性。这当然是很难发现的 没什么别的了。

我的猜测/希望开发工具能够帮助解决这个问题,通过简单地通过视觉(不同的颜色,更强烈的字体等)来容易地识别不可变类型。但我认为这就是解决办法改变所有的方法名。


为了尽可能清楚,您可能需要使用更冗长的CopyAndAdd或类似的东西。


我喜欢和()。我认为它最不可能含糊不清。我能想到的唯一冲突是逻辑上的,我不认为这是c#开发人员的问题,即使是VB,我认为上下文使它不太可能引起问题,任何问题都会在编译时迅速被发现。在英语中,“对这些和那个做点什么”或“把这些和那个放进盒子里”也很有效。

我想用()就可以了。我担心的是,它可能看起来有点像linq Where<>方法,特别是如果有一个lambda作为参数。我脑子里的英文也不太清楚,尤其是“Do something to These With That”。

我不喜欢. plus()。我不能忘记它是Add的同义词:加号=加号= + = Add。


c#中的DateTime使用Add。所以为什么不使用相同的名称呢?只要类的用户理解类是不可变的。


一些随意的想法:

ImmutableAdd () Append () ImmutableList<T>(ImmutableList<T> originalList, T newItem)构造函数


我觉得"加"或"加"听起来不错。列表本身的名称应该足以传达列表的不可变性。


“替代”?它不会添加到列表中,而是用一个新的列表替换该列表。


我将其命名为Extend()或者ExtendWith()如果您觉得非常冗长的话。

扩展意味着在不改变的情况下将某物添加到另一物。我认为这是c#中非常相关的术语,因为它类似于扩展方法的概念——它们“添加”一个新方法到一个类中,而不“触及”类本身。

否则,如果你真的想强调你根本没有修改原始对象,使用一些像Get-这样的前缀在我看来是不可避免的。


我会选择简单的Add()。如果您希望传达这实际上是一个收集操作,则可以使用Append()。

除了显式方法之外,我仍然建议实现重载的+操作符。这是一个众所周知的行动。每个人都知道String是不可变的,但每个人都使用“+”来构建它的新实例。


附加-因为,请注意系统的名称。字符串方法建议它们改变实例,但实际上并没有。

或者我很喜欢afterapppending:

void test()
{
  Bar bar = new Bar();
  List list = bar.AfterAppending("foo");
}

我喜欢迈尔斯关于CopyAndAdd的建议。为了保持“突变”的主题,也许你可以用芽(无性繁殖)、生长、复制或进化?=)

编辑:继续我的基因主题,“生殖”如何,这意味着一个新的物体是在前一个物体的基础上制造的,但添加了一些新的东西。


“增强”怎么样?

它与Add是不同的单词,但它是一个接近的同义词。


我认为你想要得到的很难表达的关键是非排列,所以可能是带有生成词的东西,比如CopyWith()或InstancePlus()。


事实上,我喜欢And,尤其是在惯用的方式。我特别喜欢它,如果你有一个空列表的静态只读属性,也许使构造函数私有,所以你总是必须从空列表构建。

var list = ImmutableList<string>.Empty.And("Hello")
                                      .And("Immutable")
                                      .And("Word");

由于类型名是ImmutableList,因此指定它实际上是不可变的,我认为. add()是很好的。然而,如果你真的坚持使用其他东西,我可能会使用像. addex()这样的东西,其中Ex意味着扩展,并暗示用户应该在使用前确定Ex是什么(通过阅读文档)。我也喜欢Plus()和GetCopyWith()的建议


我不认为英语语言会让你在使用与“添加”意思相同的动词时,以一种明确无误的方式暗示不变性。“加”几乎就可以了,但人们仍然会犯错误。

防止用户将对象误认为可变对象的唯一方法是通过对象本身的名称或方法的名称(如“GetCopyWith”或“CopyAndAdd”等详细选项)显式地使其显式。

所以就用你最喜欢的"加"吧


每当我在命名法上遇到麻烦时,我就会上网。

Thesaurus.com返回"add":

定义:相邻,增加;使 进一步的评论 同义词:词缀, Annex, ante, append, augment, beef 向上,提升,建立,充电, 继续,插入,插入,肉体 出去,热身,徒步,徒步,搭顺风车, 勾上,勾搭上,包括,杰克 振作起来,振作起来,团结起来, 利用,背上,插入,倒入 上,回答,跑上去,进一步说,拍 雪球,煮汤,加速, Spike, step up, supplement, sweet, 标签,标签

我喜欢Adjoin的发音,或者更简单地说,Join。这就是你在做的,对吧?该方法也可以应用于加入其他ImmutableList<>。


就我个人而言,我喜欢. with()。如果我正在使用该对象,在阅读文档或代码注释后,就会清楚它的功能,在源代码中读起来也不错。

object.With("My new item as well");

或者,你可以加上“Along”。:)

object.AlongWith("this new item");

我会选择Cons,原因很简单:它的意思正是你想要的。

I'm a huge fan of saying exactly what I mean, especially in source code. A newbie will have to look up the definition of Cons only once, but then read and use that a thousand times. I find that, in the long term, it's nicer to work with systems that make the common case easier, even if the up-front cost is a little bit higher. The fact that it would be "meaningless" to people with no FP experience is actually a big advantage. As you pointed out, all of the other words you found already have some meaning, and that meaning is either slightly different or ambiguous. A new concept should have a new word (or in this case, an old one). I'd rather somebody have to look up the definition of Cons, than to assume incorrectly he knows what Add does. Other operations borrowed from functional languages often keep their original names, with no apparent catastrophes. I haven't seen any push to come up with synonyms for "map" and "reduce" that sound more familiar to non-FPers, nor do I see any benefit from doing so.

(完全披露:我是一个Lisp程序员,所以我已经知道Cons的意思。)


. trail表示对列表的理解非常强,没有改变,这个对象尾随在列表后面,它没有被添加到列表中。

var list = new ImmutableList<string>().Trail("Hello");
                                      .Trail("immutable");
                                      .Trail("word");

也许有一些词让我记得更多的是复制和添加东西,而不是突变实例(如“Concatenate”)。但我认为在其他行为上有一些对称的话也会很好。我不知道一个类似的词“删除”,我认为类似于“连接”。“加上”对我来说听起来有点奇怪。我不希望它用于非数值上下文中。但这也可能是因为我的非英语背景。

也许我会用这个方案

AddToCopy
RemoveFromCopy
InsertIntoCopy

不过,当我想到这些时,它们也有自己的问题。人们可能会认为他们在给定的论证中删除了一些东西或添加了一些东西。我一点也不确定。我认为,这些话也不适合用在捆绑上。太啰嗦了,没法打字。

也许我会用简单的“添加”和“朋友”。我喜欢它在数学中的应用

Add 1 to 2 and you get 3

Well, certainly, a 2 remains a 2 and you get a new number. This is about two numbers and not about a list and an element, but i think it has some analogy. In my opinion, add does not necessarily mean you mutate something. I certainly see your point that having a lonely statement containing just an add and not using the returned new object does not look buggy. But I've now also thought some time about that idea of using another name than "add" but i just can't come up with another name, without making me think "hmm, i would need to look at the documentation to know what it is about" because its name differs from what I would expect to be called "add". Just some weird thought about this from litb, not sure it makes sense at all :)


“添加()”

它返回一个新列表的事实是无关紧要的,这是签名所揭示的实现细节。该方法完成的主要操作是向列表中“添加”一个新元素。它如何执行或返回什么不应该是方法名称的一部分。如果一个方法是同步的,是否会影响方法名-"synchronizedAdd()"??? -当然不是。

像String这样的类遵循可能的突变模式,仍然有非常简单的方法名——没有一个是复合词。


在这种情况下,我通常会选择孔卡特。对我来说,这通常意味着正在创建一个新对象。

var p = listA.Concat(listB);
var k = listA.Concat(item);

我认为这可能是极少数可以接受重载+操作符的情况之一。在数学术语中,我们知道+不会在另一项的末尾附加一项。它总是将两个值组合在一起并返回一个新的结果值。

例如,直觉上很明显,当你说

x = 2 + 2;

x的结果值是4,而不是22。

同样的,

var empty = new ImmutableList<string>();
var list1 = empty + "Hello";
var list2 = list1 + "immutable";
var list3 = list2 + "word";

应该清楚每个变量将持有什么。应该清楚的是,list2在最后一行中没有改变,而是将list3分配为将“word”附加到list2的结果。

否则,我将把函数命名为Plus()。


首先,一个有趣的起点: http://en.wikipedia.org/wiki/Naming_conventions_(编程)……特别是,查看底部的“See Also”链接。

我支持正数或正数,实际上是相等的。

Plus和and在词源上都是基于数学的。因此,两者都包含数学运算;两者都会生成一个表达式,该表达式读起来很自然,可以解析为一个值,该值符合具有返回值的方法。和具有额外的逻辑内涵,但这两个词都可以直观地应用于列表。Add表示对对象执行的操作,这与方法的不可变语义相冲突。

两者都很短,考虑到操作的原始性,这一点尤为重要。简单的、经常执行的操作应该有更短的名称。

我更喜欢通过上下文来表达不可变的语义。也就是说,我宁愿简单地暗示这整个代码块具有功能性;假设一切都是不可变的。不过,可能只有我是这样。我宁愿把不变作为准则;如果有人做过,那就是在同一个地方做过很多次;可变性是个例外。


添加(),附加()

我喜欢对不可变对象的操作使用过去时。它传达的意思是,你并没有改变原来的对象,而且当你看到它时很容易认出它。

此外,因为改变方法名通常是现在时态的动词,所以它适用于您遇到的大多数需要不可变方法名的情况。例如,一个不可变堆栈有“push”和“pop”方法。


Chain()或Attach()怎么样?


2建议:

一个“自由”函数:

Foo f = new Foo(whatever);
Foo fPlusSomething = Foo.Concat(f, something);

构造函数重载(在某种程度上,这是“自由函数”主题的变体):

Foo f = new Foo(whatever);
Foo fPlusSomething = new Foo(f, something);

对于那些住在一起的人来说,“交配”、“配偶”或“交媾”怎么样?在繁殖方面,哺乳动物通常被认为是不可改变的。

也要把联合政府赶出去。借用SQL。


加入似乎是合适的。


这可能有点夸张,但在Ruby中有一个常用的符号来区分:添加不会改变;添加!变异。如果这是您的项目中普遍存在的问题,您也可以这样做(不一定使用非字母字符,而是始终使用符号来指示突变/非突变方法)。


所以我猜一个名为“ImmutableAdd()”的方法完全太简单了?


我更喜欢加(和减)。它们很容易理解,并直接映射到涉及众所周知的不可变类型(数字)的操作。2+2不会改变2的值,它返回一个新的,同样不可变的值。

其他一些可能性:

接头()

移植()

合生()


查看http://thesaurus.reference.com/browse/add和http://thesaurus.reference.com/browse/plus,我发现了gain和affix,但我不确定它们在多大程度上意味着非突变。


那么"Stick"或者"StickTo"呢,它把一个元素粘在末尾。

或"Attach"或"AttachTo"。


我会使用构造函数。

Foo f1 = new Foo("one");
Foo f2 = new Foo(f1, "two");
Foo f3 = new Foo(f2, "three");

F1包含“1”。 F2包含“一”、“二”。 F3包含“one”、“two”、“three”。


list.CopyWith(元素)

Smalltalk也是一样:)

还有list.copyWithout(element),它删除一个元素的所有出现,这在用作list.copyWithout(null)来删除未设置的元素时非常有用。


我来晚了一点,NewWith怎么样?


我把它叫做ToInclude

var empty = new ImmutableList<string>();
var list1 = empty.ToInclude("Hello");
var list2 = list1.ToInclude("immutable");
var list3 = list2.ToInclude("word");

惯用地(?)

var list = new ImmutableList<string>().ToInclude("Hello");
                                      .ToInclude("immutable");
                                      .ToInclude("word");

对你提到的案子也适用。

var list = new ImmutableList<string>();list.ToInclude("foo");

var suite = new TestSuite<string, int>();suite.ToInclude(x => x.Length);

任何暗示将返回相同类型的对象的名称都可以使用。Plus是一个很好的名称,就好像你加了两个对象,希望返回结果。

不过,在这种情况下,Plus听起来不像是使用的正确名称,因为您正在将一个测试“Plus”到一个测试套件中。

对我来说,GetWith()听起来像是一个选项。或者GetTypeWith(),其中类型显然是您使用的类型。例如:

var list = new ImmutableList<String>();
var list2 = list.GetWith("First");
var list3 = list2.GetWith("Second");

// OR

var list2 = list.GetListWith("First");

Get表示您正在获取已经包含的列表,With表示您还需要另一个对象。CopyWith()也符合这个标准。

我认为GetWith的直接问题是它不容易被猜测。开发人员想要添加一个套件,而不是获得当前的套件。我会立即输入。add,希望情报显示的东西非常接近我的预期。


游戏开始得很晚,但是《冰雪奇缘》怎么样。在WPF中,使用Freeze和IsFrozen来测试对象是否可变是有优先级的。当然,这有点扭曲了意义,因为通常Freeze()是一种使当前对象不可变的方法,但如果它有一个参数,您可以看到您得到的是不可变的东西。

var list = new ImmutableList<string>().Freeze("Hello")
                                      .Freeze("Fridgid")
                                      .Freeze("World");

基本上:

这是一个词 其内涵围绕着不变性展开。 WPF中“相似”语法的优先级。


扩展方法呢?在这种情况下,可以将其称为Join。作为一个扩展方法,用户应该知道它是一个静态方法,因此可能会让他们暂停一下,并鼓励他们查看返回值。同时,您拥有“实例”方法的可用性。

public static ImmutableList<T> Join(this ImmutableList<T> body, T tail)
{
    // add robust error checking in case either is null...
    return new ImmutableList<T>(body, tail);
}

再后来……

var list = new ImmutableList<string>().Join("Hello")
                                      .Join("Extensible")
                                      .Join("World");

我不太清楚发布多个答案的普遍行为,但这是一个有趣的问题,因为我认为命名是设计中的关键步骤,我的大脑一直在思考这个问题。


我会选择Add,因为我可以看到一个更好的名称的好处,但问题是要为每个其他不可变操作找到不同的名称,如果这样做有意义的话,可能会使类非常陌生。


我认为Plus()和Minus()或include()、excluded()在暗示不可变行为方面是合理的。

然而,任何命名选择都不会让每个人都完全清楚,所以我个人认为,一个好的xml文档注释将在这里发挥很大作用。当你在IDE中编写代码时,VS会把这些问题扔到你的脸上——它们很难被忽视。


c#伪代码如下:

interface Foo
{
    // Constructors
    Foo();
    Foo(params Foo[] foos);

    // Instance method
    Foo Join(params Foo[] foos);

    // Class method
    static Foo Join(params Foo[] foos);
}    

所以你可以这样调用:

var f0 = new Foo();
var f1 = new Foo(new Foo(), new Foo(), new Foo());
var f2 = Foo.Join(new Foo(), new Foo(), new Foo());
var f3 = f0.Join(new Foo(), new Foo(), new Foo());
var f4 = new Foo(new Foo(new Foo()), new Foo(), new Foo(new Foo()));

等等……


用Augment(或AugmentWith)方法创建包装器类怎么样?


因为这个问题现在基本上是一个同义词库:. bring()怎么样?也就是说,给我这个列表,然后把这个元素带来?

Foo = List.Bring('short');
          .Bring('longer');
          .Bring('woah');

虽然不容易脱口而出,但对我来说很重要。

实际上,AndBring()可能更好。


也许混淆源于您想要两个操作合一的事实。为什么不把它们分开呢?DSL风格:

var list = new ImmutableList<string>("Hello");
var list2 = list.Copy().With("World!");

Copy会返回一个中间对象,这是原始列表的可变副本。With将返回一个新的不可变列表。

更新:

但是,使用中间的、可变的集合并不是一个好方法。中间对象应该包含在Copy操作中:

var list1 = new ImmutableList<string>("Hello");
var list2 = list1.Copy(list => list.Add("World!"));

现在,Copy操作接受一个委托,该委托接收一个可变列表,因此它可以控制复制结果。它能做的远不止追加一个元素,比如删除元素或对列表排序。它还可以在ImmutableList构造函数中使用,在没有中间不可变列表的情况下组装初始列表。

public ImmutableList<T> Copy(Action<IList<T>> mutate) {
  if (mutate == null) return this;
  var list = new List<T>(this);
  mutate(list);
  return new ImmutableList<T>(list);
}

现在不存在被用户误解的可能性,他们自然会掉进成功的坑里。

还有一个更新:

如果你仍然不喜欢提到可变列表,即使现在包含了它,你可以设计一个规范对象,它将指定或脚本,复制操作将如何转换它的列表。用法是一样的:

var list1 = new ImmutableList<string>("Hello");
// rules is a specification object, that takes commands to run in the copied collection
var list2 = list1.Copy(rules => rules.Append("World!"));

现在你可以对规则名称进行创新,你可以只公开你想要Copy支持的功能,而不是IList的全部功能。

对于链接的使用,你可以创建一个合理的构造函数(当然,它不会使用链接):

public ImmutableList(params T[] elements) ...

...

var list = new ImmutableList<string>("Hello", "immutable", "World");

或者在其他构造函数中使用相同的委托:

var list = new ImmutableList<string>(rules => 
  rules
    .Append("Hello")
    .Append("immutable")
    .Append("World")
);

这里假设规则。Append方法返回这个。

这是你最新的例子的样子:

var suite = new TestSuite<string, int>(x => x.Length);
var otherSuite = suite.Copy(rules => 
  rules
    .Append(x => Int32.Parse(x))
    .Append(x => x.GetHashCode())
);

我会用运算符重载+。原因是这就是它在Python中的工作方式-列表上的.append()会改变列表,而对另一个列表执行+则会创建一个新列表。+也绝不意味着突变。我想这就是为什么你这么喜欢. plus(),所以如果你不想操作符过载,那么你可以使用. plus()。


显然,我是第一个回答这个问题的Obj-C/Cocoa人。

NNString *empty = [[NSString alloc] init];
NSString *list1 = [empty stringByAppendingString:@"Hello"];
NSString *list2 = [list1 stringByAppendingString:@"immutable"];
NSString *list3 = [list2 stringByAppendingString:@"word"];

用这个打高尔夫可赢不了。


作为一名c++程序员和STL的爱好者,我提出了add_copy。(这也会建议remove_copy, replace_copy等等)


list.copyAndAppend(elt)

听起来怎么样?)


正如前面提到的,你试图同时做两件事,并为此目的进行微优化。如果复制发生在远离原始集合定义的地方,像“Add”、“Append”这样的名称只会使人混淆。

在这种情况下,我可能会使用“CloneAppend”。直觉告诉我,我不会陷入这种境地。我相信很快您就会发现自己需要其他类型的类似操作,如“CloneFirstN”或“CloneRemoveLast”。很快你就会意识到,这样做更好:链接克隆/复制方法,添加/删除你需要的任何东西,然后将集合转换回不可变,即使这需要额外的一行代码。


就我个人而言,我会把这个方法叫做Clone,把参数叫做AdditionalValue,因为这基本上就是它看起来在做的事情,而且很容易理解。

var empty = new ImmutableList<string>(); 
var list1 = empty.Clone("Hello"); 
var list2 = list1.Clone("immutable"); 
var list3 = list2.Clone("word");

也许静态方法或操作符(是静态的)是最好的。它将从实例中剥离责任,用户将立即知道该操作不属于任何实例。特别重要的是不要使用扩展方法,因为它们在使用时与实例方法相似,这违背了目的。

静态方法可以调用Join:

public static class ListOperations {
  public static ImmutableList<T> Join<T>(ImmutableList<T> list, T tail) {
    // ...
  }
  public static ImmutableList<T> Join<T>(T head, ImmutableList<T> list) {
    // ...
  }
  public static ImmutableList<T> Join<T>(ImmutableList<T> list1, ImmutableList<T> list2) {
    // ...
  }
  // substitutes chaining:
  public static ImmutableList<T> Join<T>(ImmutableList<T> list, params T[] tail) {
    // ...
  }
}

// ....

var list = new ImmutableList<string>("Hello");
var list2 = ListOperations.Join(list, "immutable"); // inferred type parameter
var list3 = ListOperations.Join(list2, "world!");

但我更希望c#在这里有无类函数。或者至少类似于Java的静态导入功能。

运算符可以是+:

var list = new ImmutableList<string>("Hello");
var list2 = list + "immutable";
var list3 = list2 + "world!";

但我宁愿能够使用其他的东西,如<<,::或。,这在c#中是不可能的。

此外,静态成员看起来更具有功能性,我认为它们更适合这种不可变的视图。


我个人喜欢unite(),因为当你合并对象时,你仍然保留了它们的个性,但它们的联合是一个独立的新实体。并集类似于建议,但在集合论中已经有了很好的定义,而联合则更详细,对我来说,按照这种方法,我有了一个新的实体。我知道有点晚了,但是他们在清单上找不到建议。加上这个词让我想起了过去的日子,团结人们去打仗。呵呵


对于这种功能,我通常使用动词的第二种形式,在这种情况下是Added。

这个约定被Qt使用,例如QVector2D::normalized返回一个归一化的向量,而QVector2D::normalize则归一化一个向量。

以同样的方式,Added将返回带有新添加项的对象。


我会用CreateNewListWithAdditionalItems

我不介意名字太长,但这一个会告诉你它实际上会做什么。另外,如果你使用Add或任何其他更改方法,我也会抛出异常。


借用C, Concat怎么样


我个人倾向于使用AddVoid或者其他方式的VoidAdd


像String这样的方法的问题。替换是指用户会误以为发生了突变因为他可以忽略返回值。所以使用out参数作为“return”值。用户不能忽略这一点:

public class ImmutableList<T> {
  public ImmutableList(params T[] items) { 
    // ...
  }
  /// <summary>
  /// Creates a new list with the items in this list plus the supplied items.
  /// </summary>
  /// <param name="newList">
  /// The new list created for the operation.
  /// This is also the return value of this method.
  /// </param>
  /// <param name="items">Items to add to the new list.</param>
  /// <returns>The new list.</returns>
  public ImmutableList<T> Add(out ImmutableList<T> newList, params T[] items) {
    // ...
  }
}

用法:

var list = new ImmutableList<string>("Hello", "Immutable");
ImmutableList<string> newList;
list.Add(out newList, "World");

cloneWith怎么样?

原因: 1. 对于建议的单个单词名称,在检查文档之前,我无法确定它们是做什么的。 2. 对于到目前为止建议的复合名称,我更喜欢cloneWith better。我将确切地知道我从这个方法名中得到了什么,它相对简洁,容易记住,而且它只是“感觉”对:) 3.随着大多数编程工具中的现代代码完成,较长的名称比以往任何时候都更容易使用。

虽然很多时候简洁会增加清晰度,但如果你必须选择的话,要选择清晰度而不是简洁性。

在行动:

var empty = new ImmutableList<string>();
var list1 = empty.cloneWith("Hello");
var list2 = list1.cloneWith("immutable");
var list3 = list2.cloneWith("word");

如果你是一个函数式程序员,它有几个名字:

(++)

明显的“附加”。也可以是concat,这取决于你的类型:

-- join two things into one thing:
append :: a -> a -> a    

-- join many things into one thing
concat :: [a] -> a

或者你可能会说(:),又名cons:

(:) :: a -> [a] -> [a]    

如果你在列表的前面加入东西,那么在列表的末尾加入snoc。

至少在过去的20年里,在haskell的世界里,我们一直这样称呼添加到列表上的东西。


注意,这不是算术(+),因为它是单面的,不是一个环。


WithAdded这个名字读起来很好,在我看来比Added要好,它可以被理解为一个形容词。

var list2 = list1.WithAdded("immutable");

或者是稍微啰嗦一点的版本:

var list2 = list1.WithAddedElement("immutable");

我有点晚了,但我没有看到一个答案,建议根本不做!让它保持不变。当他们把你的方法放入循环时,你所使用的任何方法都会被滥用,这最终会发生。相反,我建议使用建造器。

MyImmutableBuilder builder = new MyImmutableBuilder(someImmutable);
MyImmutable modifiedImmultable = builder.add(element1).add(element2).build();

在ImmutableList的情况下,你的构建器很可能只是一个List。

I guess it all boils down to... to you really want an easy way to add just a single element to your immutable collection? If it's a common occurrence, you probably don't want to be using an immutable object (you can always call build() any time you want to send a snapshot of the object at it's present state...) If it's not a common occurrence, then having a builder as a requirement to do it wouldn't be a significant impediment, especially if it were documented that this is how you want to build them.

@supercat -考虑你描述的以下场景:

Stack a = someImmutableStackOfSize(10);
Stack b = a.popAndCopy(); // size 9
Stack c = a.popAndCopy(); // also size 9
// a b and c have the same backing list
Stack d = b.pushAndCopy("node1");
Stack e = c.pushAndCopy("node2");
// because d and e had the same backing list, how will it 
// know that d's last element is node1, but e's last
// element is node2?

This seems to come down to finding a word that expresses that the object is not modified. Also, that it is cloned and the element passed as a parameter is added to the end of the cloned list. Standard names like add and clone are never going to cover it. Perhaps clone_and_append which is also rather ambiguous, since the append part might imply that the parameter is appended to the original list. So it should probably be something like clone_and_append_to_clone or better yet append_to_clone, although this one does not really imply that the clone is going to be returned by this method, but rather that the clone already exists as part of the origina list.

因此,更多地考虑寻找一个不意味着对原始列表进行修改的名称,并建议创建一个新列表,考虑以下内容:

Offshoot -- var list1 = list.Offshoot("tail") - in the strictest sense, offshoot refers here to the new element and not the entire new list. Another apparent disadvantage is that it is not an action, the more correct name being list.CreateOffshoot. However, I find it gives a clear indication of the fact that the new list will contain the original list and that the new element will come at the end of the list. Spawn -- var list1 = list.Spawn("tail") - similar to the previous. The advantage is that it is an action, but there is a weaker implication that the new list will contain the original list. BranchOff -- var list1 = list.BranchOff("tail") - an action that suggests the original list will be cloned. One potential ambiguity is that it is not very clear how the parameter element will be used.


copyWithSuffix:将是一个自定义方法名,类似于Objective-C的不可变字符串复制方法stringByAppendingString:。

var suite = new TestSuite<string>();
suite = suite.CopyWithSuffix("Str1")
             .CopyWithSuffix("Str2")
             .CopyWithSuffix("Str3");

后缀传达了它的附加性,而Copy则明确了它的不可变性。您可以自由地将Suffix替换为更符合测试套件实际类型的东西(也许您没有将这些后缀添加到一个大的内部字符串中,您可以提供我所知道的测试用例名称/参数)。


1)问题分析 问题的摘要可以是: 从一个不可改变的…获得一个新的不可变的,但与原来的不同。 在当前问题的语境中: 从一个不可变列表中获得一个新的不可变列表,该列表最后包含一个新元素。 你能否给出一个有效的“动词”(编程语言中的“方法”),以简洁的方式表达动作,而不使用简单的“添加”动词?

2) Proposal analysis First concept : Immutable give a similar immutable , >>"copy" verb express conservation in "content" but less for it defined constraints (immutable) >>"clone" verb express conservative in "content" but also for its defined constraints. >>"twin" verb is similar to clone but not common in software , but it's short and sound good. Second concept : the result give something different from the original >>"add" express an action to make the difference between original and the new object but by definition we do not act (mutate) an immutable. >>"plus" express the fact that verb result will be augmented without disturbing the original object ... "adverb" approach is more "immutable" friendly >>"with" as before it express the fact that "verb" will do something more but may be ambigous of what it will doing more. Can also express the fact the "verb parameter" is the "more" of the "verb" 3) Answers With verb "clone" >> augmentClone >> growClone >> extendClone with "with" >> cloneWith >> augmentCloneWith >> growCloneWith >> extendCloneWith With verb "twin" >> augmentTwin >> growTwin >> extendTwin with "with" >> twinWith >> augmentTwinWith >> growTwinWith >> extendTwinWith


我会使用list. newlist(“word”),因为它描述了你正在做的事情(创建一个新列表)。

我必须同意,这并不意味着参数被添加到列表的末尾,但就我个人而言,我属于简洁的阵营,所以我更喜欢list. newlist ("word")而不是list. newlistafterappend ("word")。


就我个人而言,如果你追求流畅的风格,我会选择Pad(..),也可以使用AndWith(..)的扩展方法。

var list = new ImmutableList<string>().Pad("Hello")
                                      .AndWith("immutable")
                                      .AndWith("word");

我知道这似乎是在徒劳无益,但我认为您可能会以一种奇怪的方式来处理这个问题(因此命名困难)。当我有一个不可变的对象时,我通常不期望该对象具有暗示可变性的函数(如Add或AddAll)。我知道字符串和DateTime这样做,但即使我已经成为受害者,忘记使用DateTime / String函数的结果,所以我会尽可能避免这些陷阱。

如果我是您,我会遵循类似于Guava使用的Builder模式。参见Guava中的不可变集合。基本上,这个概念是用它所包含的所有内容来构造不可变对象。如果你想要创建一个新的不可变对象,你需要使用一个新的构建器。

var suite = new TestSuite.Builder<string, int>().add(newTest).build();

在用户调用构建之前,您可以扩展构建器以允许对测试套件进行各种修改(如果您愿意,可以使用标准列表命名约定,如添加/删除)。


一个很晚的回答,但我想补充我的观点:我认为这里的重点可能是动词时态而不是动词本身:

var output= myList.joinedWith("Element"); //or AddedTo, ConcatenatedTo...

在我看来,分词改变了含义:我们没有向myList添加任何东西,而是返回操作的结果。


虽然正确命名方法可以减少误用的机会,但如果有一些语言构造告诉编译器:“这个方法什么都不做,只是返回一个新对象,它应该被使用,否则方法调用是没有意义的”,那么每当有人在错误的设置中使用该方法时,编译器就会报错/警告。

我已经为此填写了一个功能请求,如果你同意,可以自由投票。


支持Plus的另一点是新的几乎不可变的Java时间API,其中Plus()用于添加到Instants。


除了名称之外,不可变容器集合是操作符重载的经典情况(int/string/etc已经是这种情况)。这里有两个优势:

简洁:l1 = l + o1 + o2而不是l1 = l。add (o1).Add(o2) 使用赋值运算符:l += o而不是l = l。

使用操作符也使代码更不容易出错,当从可变类型切换到不可变类型时,通常会忘记赋值,只写ie l.Add(o);(会带来灾难性的后果)但只要写l+o;不使用的结果真的很突出。

您可以在作为F包一部分的集合中看到这一点。


这些东西的一致性很重要,我喜欢遵循Swift API设计指南中的命名约定:

Name functions and methods according to their side-effects Those without side-effects should read as noun phrases, e.g. x.distance(to: y), i.successor(). Those with side-effects should read as imperative verb phrases, e.g., print(x), x.sort(), x.append(y). Name mutating/non-mutating method pairs consistently. A mutating method will often have a non-mutating variant with similar semantics, but that returns a new value rather than updating an instance in-place. When the operation is naturally described by a verb, use the verb’s imperative for the mutating method and apply the “ed” or “ing” suffix to name its non-mutating counterpart. Mutating Non-mutating x.sort() z = x.sorted() x.append(y) z = x.appending(y) When the operation is naturally described by a noun, use the noun for the non-mutating method and apply the “form” prefix to name its mutating counterpart. Non-mutating Mutating x = y.union(z) y.formUnion(z) j = c.successor(i) c.formSuccessor(&i)

因为该方法的变异版本是Add,所以非变异版本可以是Added或Adding:

// Note that following the convention results in code being written as english sentences
var empty = new ImmutableList<string>();
var list1 = empty.Adding("Hello");
var list2 = list1.Adding("immutable"); // `list2` is equal to `list1` adding "immutable"
var list3 = list2.Adding("word");