2014-03-02 88 views
2

阅读有关后增量和增量前的几个问题我需要尝试解释一个新的程序员在什么情况下我实际上需要一个或另一个。在什么类型的情况下,应用增量后运算符,以及应用预增量运算符更好。使用增量前或增量后运算符的情况

这是教案例研究,在特定的代码中,为了获得某些任务的特定值,需要应用其中一个或另一个。

+3

您是否专门提出函数式编程?我看到你添加了FP标签,但在你的文本中,FP从未被提及过。这对我来说非常混乱,因为Pre-I vs Post之后的差异在变量可变的语言中对我来说似乎要大得多。 –

+0

不是专用于功能,但它是唯一出现的标签。实际上,我正在寻找更通用的标签。 –

+0

您可以使用自动建议标签以外的标签。 – user2357112

回答

5

简短的回答是:你永远需要他们!

长久的答案是,早期的微型计算机的指令集具有这样的功能。在读取一个存储单元时,您可以在读取该单元后进行增量或预先减量。这种机器级别的特征激发了C的前身,从它发现它的方式到更新的语言。

为了理解这一点,人们必须记住,那时RAM非常稀少。当你的程序有64k可寻址内存时,你会发现编写非常紧凑的代码是值得的。那些日子的机器架构通过提供非常强大的指令反映了这种需求。鉴于sj是在寄存器中只有一个指令

s = s + a[j] 
j = j + 1 

,:因此,你可以表达类似代码。

因此,我们必须用C,允许不费力的编译器生成高效的代码行的语言特性:

register int s = 0;   // clr r5 
s += a[j++];     // mov j+, r6 move j to r6 and increment j after 
           // add r5,a[r6] 

这同样适用于像+=的快捷操作,-=*=

他们

  1. 的一种方式,以节省打字
  2. 一种用于编译器,它必须适应在小RAM的帮助,买不起太多的优化

例如,

a[i] *= 5 

这是短期的

a[i] = a[i] * 5 

生效保存编译器某种形式的常见子表达式分析。

然而,所有的语言功能,总是可以替换为等效的,也许更长的代码,不使用它们。现代编译器应该将它们转换为高效的代码,就像更短的表单一样。

因此,底线和您的问题的答案:你不需要寻找一个需要来应用这些运营商的情况。这种情况根本不存在。

+0

+1指出内存节省,谢谢! – coma

+2

不,'++'和'--'不受PDP-11的启发。 PDP-11具有预处理和后处理的寻址模式,但不是后降序和预增量 - 当将++ ++和'--'运算符添加到C的前身语言B时,则不存在。 –

+0

@KeithThompson我已经重写有一点,我希望现在更准确。 – Ingo

3

那么,有些人喜欢Douglas Crockford反对使用这些操作符,因为它们可能会导致意外的行为,隐藏未经训练的眼睛的最终结果。

但是,因为I'm using JSHint,让我们在这里分享一个例子:

http://jsfiddle.net/coma/EB72c/

var List = function(values) { 

    this.index = 0; 
    this.values = values; 
}; 

List.prototype.next = function() { 

    return this.values[++this.index]; 
}; 

List.prototype.prev = function() { 

    return this.values[--this.index]; 
}; 

List.prototype.current = function() { 

    return this.values[this.index]; 
}; 

List.prototype.prefix = function(prefixes) { 

    var i; 

    for (i = 0; i < this.values.length; i++) { 

     this.values[i] = prefixes[i] + this.values[i]; 
    } 
}; 

var animals = new List(['dog', 'cat', 'parrot']); 

console.log('current', animals.current()); 
console.log('next', animals.next()); 
console.log('next', animals.next()); 
console.log('current', animals.current()); 
console.log('prev', animals.prev()); 
console.log('prev', animals.prev()); 

animals.prefix(['Snoopy the ', 'Gartfield the ', 'A ']); 

console.log('current', animals.current()); 
2

正如其他人所说,你永远不会“需要”++--运营商的味道。再说一遍,有许多语言的特性是你从不“需要”的,但是这些特性在清楚地表达代码的意图方面很有用。你不需要一个返回一个值的赋值,或者一元否定,因为你总是可以编写(0-x) ...... heck,如果你把它推到极限你根本不需要C,因为你总是可以用汇编写入,你不需要汇编程序,因为你总是可以设置位来构造指令...

(提示弗兰克海斯的歌,当我是男孩。然后下车我的草坪,你!儿童)

因此,这里真正的答案是使用增量和减量运算需要它的文体 - 在被操纵的价值在某种意义上是一个计数器,并在有意义推进计数“一世通过“。循环控制是一个明显的地方,人们期望看到增量/减量,并且它比替代方案更清楚地阅读。并且有很多C语言成为语言中的元语言,因为它实际上是使用的 - 例如strcpy()的经典单行版本,并且有经验的C程序员可以一眼就识别出来,能够根据需要重新创建;其中许多人利用增加/减少作为副作用。

不幸的是,“它在风格上有意义”并不是一个简单的教导规则。就编码风格的其他方面而言,它确实需要来自其他人的代码,以及对编程语言的“母语”如何思考代码的理解。

这不是一个非常令人满意的答案,我知道。但我不认为有更好的存在。

+0

一个教学方法是首先不教授短手,但后来,当学生发现他们自己编写的代码像'int temp = counter; counter = counter + 1;返回一个[temp]'你可以告诉他们他们可以写'返回一个[counter ++]:' – Ingo

+1

我认为你需要教大家短信存在,因为他们肯定会看到他们在示例代码中来你有问题。但我同意,把它们作为速记而不是作为主要解决方案是有意义的,并且只将注意力集中在“柜台”而不是一般操作上,并提醒人们任何带有副作用的操作都会提高优先级和执行顺序的问题,它通常比避免深入理解更好。 “真正的作家改写,以避免这个问题。” – keshlam

0

通常,这取决于您在特定情况下需要什么。如果你操作的结果是需要这只是你在增加/减少之前还是之后需要变量值的问题。因此,请使用使代码更清晰明白的代码。

在像C一些语言++它被认为是很好的做法,使用预增量/预减量运算符,如果你不需要的值,因为后运营商需要存储在以前的值在操作过程中是暂时的,因此它们需要更多指令(额外的副本),并且可能会导致复杂类型的性能问题。

我不是C专家,但我不认为它真的很重要你在C中使用,因为没有大型结构的增量/减量运算符。