更好、更简单的“语义冲突”例子?
我喜欢区分版本控制系统 (VCS) 中的三种不同类型的冲突:
- 文本
- 句法
- 语义
文本冲突是由合并或更新过程检测到的冲突。这是由系统标记的。在解决冲突之前,VCS 不允许提交结果。
VCS 不会标记语法冲突,但结果将无法编译。因此,即使是稍微细心的程序员也应该明白这一点。 (一个简单的例子可能是通过Left重命名变量,并通过Right使用该变量添加一些行。合并可能会有一个未解析的符号。或者,这可能会引入一个变量隐藏导致的语义冲突。)
最后,VCS 不会标记语义冲突,结果可以编译,但代码运行时可能会出现问题。在轻微的情况下,会产生不正确的结果。在严重的情况下,可能会引发崩溃。即使这些也应该由非常细心的程序员在提交之前通过代码审查或单元测试来检测。
我的语义冲突示例使用了 SVN (Subversion) 和 C++,但这些选择与问题的本质并不真正相关。
基本代码为:
int i = 0;
int odds = 0;
while (i < 10)
{
if ((i & 1) != 0)
{
odds *= 10;
odds += i;
}
// next
++ i;
}
assert (odds == 13579)
左 (L
) 和右 (R
) 更改如下。
左的“优化”(更改循环变量采用的值):
int i = 1; // L
int odds = 0;
while (i < 10)
{
if ((i & 1) != 0)
{
odds *= 10;
odds += i;
}
// next
i += 2; // L
}
assert (odds == 13579)
右的“优化”(更改循环变量的使用方式):
int i = 0;
int odds = 0;
while (i < 5) // R
{
odds *= 10;
odds += 2 * i + 1; // R
// next
++ i;
}
assert (odds == 13579)
这是结果合并或更新,并且未被 SVN 检测到(这是 VCS 的正确行为),因此它不是文本冲突。请注意,它可以编译,因此不是语法冲突。
int i = 1; // L
int odds = 0;
while (i < 5) // R
{
odds *= 10;
odds += 2 * i + 1; // R
// next
i += 2; // L
}
assert (odds == 13579)
assert
失败,因为 odds
是 37。
所以我的问题如下。还有比这个更简单的例子吗?是否有一个简单的示例,其中已编译的可执行文件出现新的崩溃?
作为第二个问题,您在实际代码中是否遇到过这种情况?再次强调,简单的例子特别受欢迎。
I like to distinguish three different types of conflict from a version control system (VCS):
- textual
- syntactic
- semantic
A textual conflict is one that is detected by the merge or update process. This is flagged by the system. A commit of the result is not permitted by the VCS until the conflict is resolved.
A syntactic conflict is not flagged by the VCS, but the result will not compile. Therefore this should also be picked up by even a slightly careful programmer. (A simple example might be a variable rename by Left and some added lines using that variable by Right. The merge will probably have an unresolved symbol. Alternatively, this might introduce a semantic conflict by variable hiding.)
Finally, a semantic conflict is not flagged by the VCS, the result compiles, but the code may have problems running. In mild cases, incorrect results are produced. In severe cases, a crash could be introduced. Even these should be detected before commit by a very careful programmer, through either code review or unit testing.
My example of a semantic conflict uses SVN (Subversion) and C++, but those choices are not really relevant to the essence of the question.
The base code is:
int i = 0;
int odds = 0;
while (i < 10)
{
if ((i & 1) != 0)
{
odds *= 10;
odds += i;
}
// next
++ i;
}
assert (odds == 13579)
The Left (L
) and Right (R
) changes are as follows.
Left's 'optimisation' (changing the values the loop variable takes):
int i = 1; // L
int odds = 0;
while (i < 10)
{
if ((i & 1) != 0)
{
odds *= 10;
odds += i;
}
// next
i += 2; // L
}
assert (odds == 13579)
Right's 'optimisation' (changing how the loop variable is used):
int i = 0;
int odds = 0;
while (i < 5) // R
{
odds *= 10;
odds += 2 * i + 1; // R
// next
++ i;
}
assert (odds == 13579)
This is the result of a merge or update, and is not detected by SVN (which is correct behaviour for the VCS), so it is not a textual conflict. Note that it compiles, so it is not a syntactic conflict.
int i = 1; // L
int odds = 0;
while (i < 5) // R
{
odds *= 10;
odds += 2 * i + 1; // R
// next
i += 2; // L
}
assert (odds == 13579)
The assert
fails because odds
is 37.
So my question is as follows. Is there a simpler example than this? Is there a simple example where the compiled executable has a new crash?
As a secondary question, are there cases of this that you have encountered in real code? Again, simple examples are especially welcome.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
提出简单的相关示例并不明显,这个评论总结了最好的原因:
[这基本上就是你的例子所说明的]
换句话说,我在实际代码中遇到的合并后语义冲突的真实情况并不简单,而是相当复杂。
话虽这么说,最简单的例子,如 Martin Fowler 在他的文章《Feature Branch》 中所示,是方法重命名:
正如 Ole Lynge 在 他的回答(已投票),马丁Fowler今天(编辑本次编辑时)确实写了一篇关于“语义冲突”的文章,包括以下插图:
同样,这是基于函数重命名的,尽管提到了基于内部函数重构的更微妙的情况:
我认为需要在生命周期分支和功能分支之间找到一个中间立场。
如果您有一组开发人员在相同功能分支上,那么经常合并是关键。
It is not obvious to come up with simple relevant examples, and this comment sum up best why:
[Which is basically what your example illustrates]
In other words, the real case of semantic conflicts I have encountered in real code after a merge were not simple, but rather quite complex.
That being said, the simplest example, as illustrated by Martin Fowler in his article Feature Branch is a method rename:
As Ole Lynge mentions in his answer (upvoted), Martin Fowler did write today (time of this edit) an post about "semantic conflict", including the following illustration:
Again, this is based on function renaming, even though subtler case based on internal function refactoring are mentioned:
I think a middle ground needs to be found between shot-lived branches and feature-branches.
And merging often is key if you have a group of developer on the same feature branch.
场景:存在方法
foo()
。两个分支从此时开始。foo()
重命名为food()
。当分支 1 和 2 合并时,没有可检测到的冲突。但是,分支 2 对 foo() 的调用现在引用了不再存在的方法。
Scenario: A method
foo()
exists. Two branches start at this point.foo()
tofood()
.foo()
.When branches 1 and 2 merge, there is no detectable conflict. However, branch 2's call to
foo()
now references a method that no longer exists.