什么是惯用语?


19

我知道“惯用语”是一种常见的操作或模式,在某种特定语言中并没有通过核心语言语法来简化,例如整数增量:

i = i + 1;

在C ++中,此惯用法由运算符简化:

++i;

但是,当有人使用“惯用”一词时,我不确定该如何理解。是什么使一段代码“惯用”?


这在english.stackexchange.com上和在此一样多。
S.Lott

@ perl.j:有见地。您确实已经阐明了为什么“ Idiomatic”的定义不属于english.stackexchange.com。非常有帮助。
S.Lott

Answers:


24

编写某些代码的惯用方式是当您以非常特定的方式编写代码时,因为其他语言没有特定于语言的习惯用法。

例如,在C ++中,利用RAII惯用语导致编写用于管理惯用资源的C ++代码的方法。

另一个示例是在Python中使用列表推导生成列表。这是惯用的,因为您会在惯用的Python中使用列表理解功能,但是您可以使用生成器函数以任何其他语言甚至在Python中完成列表理解。

通常,尝试使用新语言而不使用特定于该语言的习惯用法的人不会编写惯用代码。


出色的描述。因此,在C#中,如果我创建一个TryAdd()在Generic Dictionary类上调用的扩展方法以首先检查!Contains()然后调用Add(),这会成语吗?我正在使用特定于C#的功能-扩展方法。
void.pointer 2011年

1
我不确定大多数经验丰富的C ++编码人员是否会告诉您编写自由函数来扩展类,而不是在可能的情况下添加成员。它与C#扩展方法不同,但是非常接近,可用于任何允许自由功能的语言。我确定的是,使用属性在C#中编写“集合/获取器”是惯用的。
克拉姆2011年

但这不是C ++,因此使用“ C ++方式”可能不够“惯用”。在这里使用扩展方法是合理的,并且非常类似于C#。无论如何,这只是一个例子,但是扩展方法对于C#来说是非常习惯的。
void.pointer 2011年

是的,这就是为什么我说“我不确定”的原因,它听起来很惯用,但与我不确定的其他语言的其他基本功能非常接近(因为我不是C#开发人员)。我还有另一个例子:循环是通过所有功能语言中的递归完成的。您可以使用其他语言来完成此操作,但显然它是功能语言的惯用语言。不确定是否有帮助。
克莱姆(Klaim)2011年

1
当然,Python通过采用一种惯用的方式来引用惯用的代码(即pythonic)而走了一步。* 8')
Mark Booth,

12

正常定义

使用,包含或表示母语使用者的自然表达

编程定义

使用,包含或表示[插入语言]程序员自然的表达式


1
如果我认为该词的“ google”定义很有用,我就不会在这里发布。我想要一个带有示例的更好的描述(类似于教程)。
void.pointer 2011年

3
@Robert-我想我不明白你为什么不明白。
2011年

2
我先看了你的答案,还不太清楚。阅读了现在标记为我的答案的答案后,很清楚“惯用的”是什么意思,我现在明白了。现在,我阅读了您的答案,这很有意义。但是,您没有带我逐步了解示例和不同的情况,这就是为什么我认为您的答案没有帮助的原因。您给了“ Webster”答复,从技术上讲是正确的,但对我的理解没有帮助。非常感谢您的帮助。
void.pointer 2011年

7

编程上下文中的惯用语通常可以定义为“用某种语言表达某些内容的最自然方式”

我看到Ruby中惯用语这个词特别多。在Ruby中,具有大量的元编程功能,并且要编写惯用的Ruby代码,通常必须使用这些功能。

请注意,仅仅因为一段代码是惯用的,并不意味着它是干净的甚至简洁的。很多时候,您必须做出让步。

因此,从根本上说,惯用语最常见是指用某种语言写东西的最常见方式,通常包括“ s语”(成语)。如果一段代码不是惯用的,那么它可能会完全可读,简洁,整洁且正确,但是在使用的语言中可能会感到/看起来很尴尬。喜欢习惯性地重写这样一段代码是否真的是一件好事,并且必须根据具体情况进行判断,这是一种偏爱

例如,惯用英语可能取决于地区。bum惯用的英国英语可能需要使用该词,而butt对于美国英语则更合适。(可悲的是我对英国英语了解不多:/)


谢谢(推荐),我想您的定义对我来说很正确!
阿沙曼·金平

3

我能想到的最好的例子是Ruby,这是我惯用的东西。

在多种语言中,您可以进行以下迭代:

for( start; end; increment){
    code;
}

或类似的东西。许多语言也有foreach(x in SetOfXes)结构。但是,Ruby特有的迭代包括以下内容:

collection.each do |current|
  current.do_stuff
end

乃至

10.times do |x|
  puts x
end

我认为这些是惯用的,因为它们代表了一种不常见的方法,并且代表了其领域哲学的核心。Ruby的哲学是与对象进行交互,尽管这种交互不是唯一的,但并不普遍。

从语义上讲,每当我听到“惯用语”一词时,我的大脑就会自动将其构想为“惯于……”-我只是反身将其解析为与特定主题有关。


如何collection.each do |current|不同foreach(current in collection)?两者都是设置current为的每个元素的循环构造collection
MSalters 2011年

2

习惯用法通常是用一种语言表达一种常见的,相对复杂的情况的最佳方法。递增不是惯用语,也不是任何种类的东西。使用前缀增量而不是后缀,可以说是C ++惯用语。

根据专家用户的决定,习惯用法是使用一种语言的最佳方法。真正的C ++习惯用法就是函数对象。另一个惯用语是RAII。语言中没有任何内容告诉您必须释放析构函数中的资源。但这是惯用法。另一个例子是模板-可以使用模板做很多事,这是惯用的,但是没有什么可以阻止您过度使用继承。


维基百科以递增为例,因此这就是我在问题中提到它的原因。为什么习惯用法必须复杂?这似乎不是基于其他答案的定义的一部分。
void.pointer 2011年

4
@Robert Dailey:增量怎么可能是成语?这是语言的一部分。习惯用法是由语言的用户制作的,它们会随着时间而变化。递增不是惯用语,而是语言的基本功能。按照这种逻辑,我可以说任何其他基本语言功能都是一个习惯用法,因此每个程序都是惯用的,事实并非如此。
DeadMG 2011年

2

您的定义不正确。习惯用法是一种书写某些其他语言可能或不可能的东西的方式,但这在该语言中很常见。通常,它比替代方法短,但这并不是必须的。

通过讨论非惯用语来解释它可能会更容易。在C ++中,编写代码很惯用:

Foo* p = SomeThingThatReturnsAFooPointer(arg, param, x, y);
if(p)
{
 // whatever
}

编写起来更惯用:

Foo* p; 
if(p = SomeThingThatReturnsAFooPointer(arg, param, x, y))
{
 // whatever
}

这段代码的作用完全相同-一些C ++的新手可能会将它读为测试,以查看p是否等于该函数返回的值,但事实并非如此。

与可能来自另一种语言的某人(非常不习惯)所写的内容相比:

Foo* p = SomeThingThatReturnsAFooPointer(arg, param, x, y);
if(p !=NULL)
{
 // whatever
}

您还将看到这些东西被敲成非惯用语:

if (x>0)
  return true;
else
 return false;

因为惯用的方法是

return (x>0);

非惯用的方法没错,但是对于那些习惯用语的人来说,它们的打字时间通常更长,阅读时间也更长。如果我称呼您为“哭泣的狼的男孩”,并且您知道这个故事,那么比我解释虚假警报如何导致人们无视您的情况要快。当然,问题是如果您不了解故事,也不知道狼与我们所说的事情有什么关系。同样,如果您以前从未见过return x<y;并且真的不知道它会做什么,则可能会遇到问题。


1
惯用的方法肯定是 if条件中声明该指针,这样,如果它不是NULL,就永远无法访问它。if(Foo* p = SomeThingThatReturnsAFooPointer(arg, param, x, y))
DeadMG 2011年

大概吧 或早于一千行,因为在整个代码块中都使用了它。或者在头文件中,因为它是成员变量。我只是声明了它,以便读者知道它是Foo *,您是对的,在现实生活中不会在这里声明它。
凯特·

1

当有多种表达方式时,最常用或普遍接受的方式。例如,在C中使用结构化语句,而不是在goto语句中使用完全相同的语句。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.