我正在学习列表的方法(pop和remove之间的区别)
并注意Command–query separation
它声明每个方法应该是执行操作的命令,或者是将数据返回给调用者的查询,而不是两者。换句话说,提出问题不应该改变答案。 [1]更正式地说,方法只有在它们是引用透明的情况下才会返回一个值,因此没有副作用。
我无法解释“提出问题不应该改变答案”,
我读了ppt
维基百科链接,其中以通道内外的“轻”标志为例
它没有说明问题和答案之间的关系。
在我看来,它应该是“回答一个问题不应该改变问题”
这是合理的说法
从理论上讲,这确立了一种理智的度量,从而可以在不同时修改该状态的情况下推理程序的状态。
我的演绎是否合理?
答案 0 :(得分:1)
你已经掌握了它的要点。在计算机科学术语中,它几乎意味着获取信息不应以任何方式修改信息
他们的意思
更正式地说,方法只有在引用透明的情况下才能返回值,因此没有副作用。
更多地与您的软件和客户之间的通信有关。这进一步说明了返回的任何信息必须是实际存储值的副本,并且不能用于以任何方式追溯到该值。
以此列表为例:
a = [1, 2, 3, 4, 5]
这是我系统中一个非常重要的列表,客户付钱给我访问我的秘密列表。我将在这里编写一个方法,并说send()
方法会将此列表发送给我的客户。
如果我send(a)
给我的客户,这打破了上面的原则。因为现在我的客户端可以使用引用中的一些令人讨厌的说法:
for i in range(len(a)):
a[i] = None
现在,当我尝试访问a时,我得到的只是[None, None, None, None, None]
。它们被覆盖了,因为我发送了它们而不是值。相反,我应该有send(a[:])
或者副本。现在无论我的客户对我发送给他的列表做了什么,它对我系统内部的内容都没有影响。
答案 1 :(得分:0)
以下课程遵守CQS原则。它的每个方法都是查询或命令。方法get
提供有关状态的信息,并且不允许任何副作用。方法set
具有副作用,因此返回None
。
class IntValue:
def __init__(self, x):
self.x = int(x)
def get(self):
return self.x
def set(self, x):
self.x = int(x)
特别是,以下情况永远如此。
v = IntValue(1)
v.get() == v.get() # Always True
您永远不会通过get
方法询问值并同时更改它。在这里,CQS原则向您保证,您可以连续多次询问该值并仍然获得相同的答案。
这一点很重要的原因是因为程序中出现了很多不受欢迎的复杂因素。
现在考虑一个封装列表的类似类。
class ListValue:
def __init__(self, x):
self.x = list(x)
def get(self):
return self.x
def set(self, x):
self.x = list(x)
上述问题是提出问题可能会导致答案改变。
v = ListValue([1, 2, 3])
lst = v.get() # [1, 2, 3]
lst.append(4)
v.get() # [1, 2, 3, 4]
在开发大型软件时,这通常是难以跟踪的错误的来源。通过确保您无法从ListValue
访问状态,但只能查看该状态的视图,可以避免这些情况。
class ListValue:
def __init__(self, x):
self.x = list(x)
def get(self):
return deepcopy(self.x) # Here we return a copy of the list
def set(self, x):
self.x = list(x)
答案 2 :(得分:0)
我无法解释“提出问题不应该改变答案”, 我阅读了ppt wikipedia链接,它以频道内外的“轻”标志为例。它没有说明问题和答案之间的关系。
Light?
基本上问“灯亮吗?”它返回True或False。
Light?
做错的事情是,如果它关闭则打开灯,或者如果它打开则关灯。在没有任何其他混淆代码的情况下,连续执行Light?
三次应返回相同的值三次。 提出问题不应该改变答案。但如果问Light?
问题改变了答案,你会得到“真,假,真”或“假,真,假”。
在我看来,它应该是“回答一个问题不应该改变问题”
不,因为答案(返回值)无法更改问题(方法调用)。但问题可以改变答案(如果你用这种方式编写方法)。