我在这里主要谈论Python,但我想这可能适用于大多数语言.如果我有一个可变对象,那么进行就地操作也是一个坏主意还会返回该对象吗?似乎大多数示例只是修改对象并返回None.例如,list.sort.
根据我的阅读,出于性能原因,某些集合的枚举器类型的设计决策是可变结构而不是引用类型.List.Enumerator是最知名的.
我正在研究一些使用数组的旧代码,并且惊讶地发现C#Arrays返回类型SZGenericArrayEnumerator作为它们的通用枚举器类型,它是一个引用类型.
我想知道是否有人知道为什么当许多其他性能关键集合使用可变结构时,Array的通用迭代器被实现为引用类型.
我目前的用例非常简单,无论是可变的还是不可变的Map都可以解决问题.
有一个采用不可变Map的方法,然后调用第三方API方法,该方法也采用不可变Map
def doFoo(foo: String = "default", params: Map[String, Any] = Map()) {
val newMap =
if(someCondition) params + ("foo" -> foo) else params
api.doSomething(newMap)
}
Run Code Online (Sandbox Code Playgroud)
有问题的地图通常很小,最多可能有一个嵌入的案例类实例列表,最多几千个条目.因此,再次假设在这种情况下对于不可变的影响很小(即通过newMap val副本基本上有2个Map实例).
不过,它让我有点唠叨,复制地图只是为了得到一张新的地图,上面贴着几个k-> v条目.
我可以params.put("bar", bar)为我想要处理的条目变为可变等等,然后params.toMap为api调用转换为immutable,这是一个选项.但后来我必须导入并传递可变映射,与使用Scala的默认不可变映射相比,这有点麻烦.
那么,对于在不可变映射上使用可变映射的合理/良好实践的一般指导原则是什么?
谢谢
编辑 所以,看起来不可变地图上的添加操作接近恒定时间,确认@ dhg和@Nicolas断言没有制作完整副本,这解决了所呈现的具体案例的问题.
说我想创建一个类car,tractor和boat.所有这些类都有一个实例,engine我想跟踪单个列表中的所有引擎.如果我正确理解电机对象是否可变,我可以将其存储为列表中的属性car以及相同的实例.
我无法找到关于用户定义的类是否可变的任何可靠信息,以及在定义它们时是否有选择可以选择,是否有人可以解释一下?
根据规范,用作哈希键的字符串被复制和冻结.其他可变对象似乎没有这么特别的考虑.例如,使用数组键,可以使用以下内容.
a = [0]
h = {a => :a}
h.keys.first[0] = 1
h # => {[1] => :a}
h[[1]] # => nil
h.rehash
h[[1]] # => :a
Run Code Online (Sandbox Code Playgroud)
另一方面,使用字符串键不能完成类似的操作.
s = "a"
h = {s => :s}
h.keys.first.upcase! # => RuntimeError: can't modify frozen String
Run Code Online (Sandbox Code Playgroud)
当涉及到散列键时,为什么字符串被设计为与其他可变对象不同?是否存在此规范变得有用的用例?该规范还有哪些其他后果?
yamlgem 阅读了一个描述哈希的手动编写的YAML文件.键可能是字符串,我想在原始YAML文件中允许不区分大小写.当我读取文件时,我可能会得到这样的哈希:
h = {"foo" => :foo, "Bar" => :bar, "BAZ" => :baz}
Run Code Online (Sandbox Code Playgroud)
我想将键规范化为小写以获得:
h = {"foo" => :foo, "bar" => :bar, "baz" => :baz}
Run Code Online (Sandbox Code Playgroud)
做这样的事情:
h.keys.each(&:downcase!)
Run Code Online (Sandbox Code Playgroud)
但由于上述原因,返回错误.
我仍然不确定struct copy或reference的规则.
我想在从数组迭代它时改变一个struct对象:例如在这种情况下我想改变背景颜色但是编译器对我大喊大叫
struct Options {
var backgroundColor = UIColor.blackColor()
}
var arrayOfMyStruct = [MyStruct]
...
for obj in arrayOfMyStruct {
obj.backgroundColor = UIColor.redColor() // ! get an error
}
Run Code Online (Sandbox Code Playgroud) 我正在学习Python,我正在处理Mutable Default Argument问题.
# BAD: if `a_list` is not passed in, the default will wrongly retain its contents between successive function calls
def bad_append(new_item, a_list=[]):
a_list.append(new_item)
return a_list
# GOOD: if `a_list` is not passed in, the default will always correctly be []
def good_append(new_item, a_list=None):
if a_list is None:
a_list = []
a_list.append(new_item)
return a_list
Run Code Online (Sandbox Code Playgroud)
我知道a_list只有在def第一次遇到语句时才初始化,这就是为什么后续调用bad_append使用相同的列表对象的原因.
我不明白的是为什么good_append有所不同.它看起来像a_list会仍然只初始化一次; 因此,该if语句仅在第一次调用函数时才为真,这意味着a_list只会[]在第一次调用时重置,这意味着它仍然会累积所有过去的new_item …
更新:好吧,现在我已经完成了它:我向微软提交了一个关于此的错误报告,因为我严重怀疑这是正确的行为.那就是说,我仍然不能100%肯定对这个问题有什么看法; 所以我可以看到什么是"正确的"是开放的某种程度的解释.
我的感觉是,微软会接受这是一个错误,或者回应一个using语句中的可变值类型变量的修改构成未定义的行为.
此外,对于它的价值,我至少猜测这里发生了什么.我怀疑编译器正在为闭包生成一个类,将局部变量"提升"到该类的实例字段; 因为它在一个using街区内,所以它正在建造这个领域readonly.正如LukeH在对另一个问题的评论中指出的那样,这会阻止方法调用,例如MoveNext修改字段本身(它们会影响副本).
注意:我已经缩短了这个问题的可读性,尽管它仍然不是很短.有关完整的原始(较长)问题,请参阅编辑历史记录.
我已经阅读了我认为是ECMA-334相关章节的内容,似乎无法找到这个问题的结论性答案.我将首先说明问题,然后为感兴趣的人提供一些附加评论的链接.
如果我有一个可实现的可变值类型IDisposable,我可以(1)调用一个方法来修改using语句中局部变量值的状态,并且代码的行为与我期望的一样.但是,一旦我在语句中的闭包内捕获了有问题的变量using,(2)在本地范围内不再可以看到对值的修改.
只有在闭包内和using语句中捕获变量的情况下,此行为才会显现; 当只有一个(using)或其他条件(闭包)存在时,这是不明显的.
为什么在using语句中的闭包内捕获可变值类型的变量会改变其本地行为?
下面是说明第1项和第2项的代码示例.两个示例都将使用以下演示Mutable值类型:
struct Mutable : IDisposable
{
int _value;
public int Increment()
{
return _value++;
}
public void Dispose() { }
}
Run Code Online (Sandbox Code Playgroud)
using块中变换值类型变量using (var x = new …Run Code Online (Sandbox Code Playgroud) 我想我可能无法理解可变集合的工作原理.我希望通过将map应用于它们或添加新元素来影响可变集合,但是:
scala> val s: collection.mutable.Seq[Int] = collection.mutable.Seq(1)
s: scala.collection.mutable.Seq[Int] = ArrayBuffer(1)
scala> s :+ 2 //appended an element
res32: scala.collection.mutable.Seq[Int] = ArrayBuffer(1, 2)
scala> s //the original collection is unchanged
res33: scala.collection.mutable.Seq[Int] = ArrayBuffer(1)
scala> s.map(_.toString) //mapped a function to it
res34: scala.collection.mutable.Seq[java.lang.String] = ArrayBuffer(1)
scala> s //original is unchanged
res35: scala.collection.mutable.Seq[Int] = ArrayBuffer(1)
//maybe mapping a function that changes the type of the collection shouldn't work
//try Int => Int
scala> s.map(_ + 1)
res36: scala.collection.mutable.Seq[Int] = ArrayBuffer(2)
scala> …Run Code Online (Sandbox Code Playgroud) 我知道适用于一般不可变类的通常原因,即
但是,包装类表示基本类型,基本类型是可变的.那么为什么封装类不可变?
mutable ×10
immutability ×3
python ×3
scala ×2
struct ×2
.net ×1
arguments ×1
arrays ×1
c# ×1
class ×1
closures ×1
coding-style ×1
collections ×1
copy ×1
enumerator ×1
function ×1
hash ×1
ienumerator ×1
java ×1
list ×1
map ×1
mutability ×1
ruby ×1
string ×1
swift ×1
use-case ×1