为什么不能有任何隐式转换?


14

据我了解,隐式转换会导致错误。

但这是没有道理的-那么正常转换是否还会导致错误?

为什么没有

len(100)

通过将语言解释(或编译)为

len(str(100))

尤其是因为(这是我知道的)唯一可行的方法。语言知道错误是什么,为什么不解决呢?

对于这个示例,我使用了Python,尽管我觉得对于这么小的东西它基本上是通用的。


2
perl -e 'print length(100);'打印3

2
因此,语言的性质及其类型系统也是如此。那是python设计的一部分。

2
它不能解决它,因为它不知道您的想法。也许您想做一些完全不同的事情。就像起诉一个循环,但从未像以前那样做过编程。因此,如果它自己修复它,那么用户既不知道自己错了,也不知道实现不会达到他的期望。
Zaibis 2015年

5
@PieCrust Python应该如何转换?所有可能的转换都将返回相同的结果是正确的。
2015年

7
@PieCrust:字符串和数组是可迭代的。为什么将strint转换为可迭代的隐式方法?怎么样range?或binhexoct)或chr(或unichr)?所有这些都返回可迭代对象,即使str在这种情况下对您而言似乎最明显。
njzk2 2015年

Answers:


37

对于它的价值len(str(100))len(chr(100))len(hex(100))都是不同的。str不是使它工作的唯一办法,因为有在Python多个不同的转换,从一个整数为字符串。当然,其中之一是最常见的,但不一定不用说那是您的意思。隐式转换的字面意思是“不用说”。

隐式转换的实际问题之一是,在存在多种可能性的情况下,哪个转换将被应用并不总是很明显,并且这会导致读者在解释代码时出错,因为他们无法找出正确的隐式转换。大家总是说他们的意图是“显而易见的解释”。对他们来说很明显,因为这就是他们的意思。对于其他人来说可能并不明显。

这就是为什么(大多数时候)Python更喜欢显式而不是隐式,而不是冒险。Python确实执行强制类型转换的主要情况是在算术中。它允许1 + 1.0因为替代办法是太烦人一起生活,但它不允许1 + "1",因为它认为你应该有指定是否意味着int("1")float("1")ord("1")str(1) + "1",或别的东西。(1,2,3) + [4,5,6]即使它可以定义规则来选择结果类型,它也不允许,就像它定义了选择结果类型的规则一样1 + 1.0

其他语言不同意并且确实有很多隐式转换。它们包含的越多,它们变得越不明显。早餐前尝试记住C标准中“整数提升”和“常规算术转换”的规则!


+1为了说明len仅适用于一种类型的初始假设在根本上是有缺陷的。
KChaloux 2015年

2
@KChaloux:实际上,在我的示例中strchr并且hex都返回相同的类型!我试图考虑一种不同的类型,其构造函数可以只接受一个int,但我还没有提出任何建议。理想的情况是一些容器X,其中len(X(100)) == 100;-) numpy.zeros似乎有点模糊。
史蒂夫·杰索普

2
有关可能出现问题的示例,请参见:docs.google.com/document/d/…destroyallsoftware.com/talks/wat
Jens Schauder

1
隐式转换(我想,这就是所谓的强制)可能会导致讨厌的东西一样有a == bb == ca == c不是真实的。
bgusach

极好的答案。我想说的一切,只是措辞更好!我唯一的小问题是,与记住JavaScript强制规则或不小心使用隐式构造函数相比,C整数提升与记住JavaScript强制规则相比非常简单。
GrandOpener 2015年

28

据我了解,隐式转换会导致错误。

您会漏掉一个字:隐式转换可能会导致运行时错误。

对于您展示的一个简单案例,您的意思很清楚。但是语言不能处理案件。他们需要遵循规则。对于许多其他情况,尚不清楚程序员是否犯了错误(使用错误的类型),或者程序员是否打算按照代码假定的意图执行操作。

如果代码假定错误,则会出现运行时错误。由于难以追踪,许多语言会错误地告诉您搞砸了,并让您告诉计算机您的真实意思(修正错误或明确进行转换)。其他语言引起了猜测,因为它们的样式适合于快速,易于调试的代码。

需要注意的一件事是,隐式转换的确会使编译器更加复杂。您需要更加谨慎地对待循环(让我们尝试从A到B的这种转换;糟糕,但是从B到A的转换!然后您还需要担心C和D大小的循环),这避免隐式转换的一个小动机。


大多数情况下是在讨论只能用于一种类型的功能,从而使转换变得显而易见。将与多种类型一起使用的哪种类型,您输入的类型会有所不同?print(“ 295”)= print(295),所以除了变量外没有什么区别。除了3d段落外,您说的都是有道理的。您能重申吗?
Quelklef

30
@PieCrust-123 +“ 456”,您要的是“ 123456”还是579?编程语言不提供上下文,因此它们很难“弄清楚”,因为他们需要知道要在其中进行添加的上下文。哪一段不清楚?
Telastyn

1
另外,也许您不想要解释为以10为底的解释。是的,隐式转换是一件好事,但是只有在它们无法隐藏错误的情况下。
Deduplicator 2015年

13
@PieCrust:说实话,我永远也不会期望len(100)返回3。我发现用它来计算表示的位数(或字节)要直观得多100
user541686

3
我与@Mehrdad在一起,从您的示例中可以很明显地看出,认为len(100)应该给100个十进制表示的字符数量是100%明显。但这实际上是您在不知道的情况下做出的大量假设其中。您可能会提出强有力的论点,为什么十六进制表示形式的位数或字节数(64-> 2个字符)应由返回len()
funkwurm

14

隐式转换是完全可能的。遇到麻烦的情况是当您不知道某件事情应该以哪种方式工作时。

在Javascript中可以看到一个示例,其中+运算符在不同的时间以不同的方式工作。

>>> 4 + 3
7
>>> "4" + 3
43
>>> 4 + "3"
43

如果参数之一是字符串,则+运算符是字符串连接,否则为加法。

如果给定一个参数并且不知道它是字符串还是整数,并且想对其进行加法运算,则可能会有些混乱。

处理此问题的另一种方法是来自Basic继承(perl遵循-请参阅“ 编程很困难,让我们开始编写脚本...”

在Basic中,len仅在String上调用该函数才有意义(Visual Basic文档:“任何有效的String表达式或变量名。如果Expression的类型为Object,则Len函数将返回其大小,因为它将被写入文件FilePut函数。”)。

Perl遵循上下文的概念。在Javascript中存在与该类型的隐式转换的混乱+运营商是有时此外,有时串联不perl的发生,因为+总是加法和.级联。

如果在标量上下文中使用某物,则其为标量(例如,使用列表作为标量,则列表的行为就好像它是一个与其长度相对应的数字一样)。如果您使用字符串运算符(eq用于相等性测试,cmp用于字符串比较),则将标量当作字符串使用。同样,如果在数学上下文中使用了某些东西(==用于相等性检验和<=>数值比较),则将标量当作数字使用。

所有编程的基本规则是“做最令人惊讶的事情”。这并不意味着那里没有惊喜,但努力是使人感到最少的惊喜。

接近perl-php的表亲,在某些情况下,操作员可以在字符串或数字上下文中对某物进行操作,而这种行为对于人们而言可能是令人惊讶的。所述++操作者是一个这样的例子。在数字上,它的行为完全符合预期。当作用于字符串(例如)时"aa",它会增加字符串($foo = "aa"; $foo++; echo $foo;print ab)。它还将翻转,以便az当增加时变为ba。这还不足为奇。

$foo = "3d8";
echo "$foo\n";
$foo++;
echo "$foo\n";
$foo++;
echo "$foo\n";
$foo++;
echo "$foo\n";

ideone

打印输出:

3d8
3d9
3e0
4

欢迎注意隐式转换和运算符在同一字符串上作用不同的危险。(Perl的句柄代码块有点不同-它决定了"3d8"++应用于运营商从一开始就是一个数值,去4的时候了(ideone) -这种行为是在很好的描述perlop中:自动递增和自动递减

现在,为什么一种语言以一种方式做某事,而另一种语言以另一种方式做事却引起了设计师的设计思想。Perl的理念是:做到这一点不止一种方法 -我可以想到许多执行其中某些操作的方法。另一方面,Python在PEP 20(Python的禅宗)中描述了一种哲学,该哲学指出(除其他事项外):“应该有一种-最好只有一种-显而易见的方法。”

这些设计差异导致了不同的语言。在Python中有一种获取数字长度的方法。隐式转换违背了这种哲学。

相关阅读:为什么Ruby没有将Fixnum隐式转换为String?


恕我直言,一种好的语言/框架通常应该有多种处理方式,以不同的方式处理常见的极端情况(最好在一种语言或框架中处理一次常见的案例,而不是在1000个程序中处理1000次);两个操作员大部分时间都在做同一件事的事实不应被认为是一件坏事,如果当他们的行为不同时,每种变化都会带来好处。比较两个数值变量xy以产生等价关系或排序的方式应该不难,但是IIRC Python的比较运算符...
supercat

...既不实现等价关系也不进行排名,并且Python没有提供任何方便的运算符。
2015年

12

ColdFusion完成了大部分工作。它定义了一组规则来处理您的隐式转换,具有单个变量类型,然后就可以使用。

结果是完全无政府状态,其中将“ 4a”添加到6为6.16667。

为什么?好吧,因为两个变量中的第一个是数字,所以结果将是数字。“ 4a”被解析为日期,并被视为“ 4 AM”。凌晨4点是4:00/24:00,或一天的1/6(​​0.16667)。加到6,您得到6.16667。

列表具有默认的逗号分隔符,因此,如果将项目添加到包含逗号的列表中,则只需添加两个项目。此外,列表是秘密字符串,因此如果列表仅包含1个项目,则可以将它们解析为日期。

字符串比较检查两个字符串是否可以首先解析为日期。因为没有日期类型,所以它可以做到。对于数字和包含数字,八进制表示法和布尔值(“ true”和“ yes”等)的字符串,情况相同

ColdFusion不会快速失败并报告错误,而是会为您处理数据类型转换。而不是一个好办法。

当然,您可以通过调用显式函数(如DateCompare ...)来修复隐式转换,但随后您就失去了隐式转换的“好处”。


对于ColdFusion,这是帮助增强开发人员能力的一种方法。尤其是当所有这些开发人员都习惯使用HTML时(ColdFusion使用标签,它称为CFML,后来他们也通过添加脚本<cfscript>,而无需为所有标签添加标签)。当您只想使某些功能正常工作时,它会很好地工作。但是,当您需要使事情以更精确的方式发生时,您需要一种语言,该语言拒绝对任何看起来可能是错误的事情进行隐式转换。


1
哇,“完全无政府状态”的确!
hoosierEE

7

您说的是,隐式转换对于明确的操作可能是个好主意,例如int a = 100; len(a),您显然要在调用之前将int转换为字符串。

但是您忘记了这些调用在语法上可能是明确的,但它们可能代表程序员想要通过的错字a1,它一个字符串。这是一个人为的示例,但是在IDE为变量名提供自动完成功能的情况下,会发生这些错误。

类型系统旨在帮助我们避免错误,对于选择严格类型检查的语言,隐式转换会破坏这种情况。

通过==执行所有隐式转换来检查Javascript的麻烦,以至于很多人现在建议坚持使用no-implicit-conversion ===运算符。


6

考虑一下您的陈述的上下文。您说这是它可以工作的“唯一方法”,但是您真的确定它会那样工作吗?那这个呢:

def approx_log_10(s):
    return len(s)
print approx_log_10(3.5)  # "3" is probably not what I'm expecting here...

正如其他人提到的,在您的非常具体的示例中,编译器理解您的意思似乎很简单。但是在更复杂的程序的更大范围内,您是要输入一个字符串,还是为另一个变量键入一个错误的名称,或者被称为错误的函数,或者其他数十种潜在的逻辑错误,通常一点都不明显。

在解释器/编译器猜出您的意思的情况下,有时它神奇地起作用,然后有时您花费数小时来调试某些没有明显原因而神秘地无法正常工作的东西。在通常情况下,被告知该声明没有任何意义,花几秒钟的时间将其更正为您的实际意思,这要安全得多。


3

隐式转换可能是一个真正的痛苦。在PowerShell中:

$a = $(dir *.xml) # typeof a is a list, because there are two XML files in the folder.
$a = $(dir *.xml) # typeof a is a string, because there is one XML file in the folder.

突然之间,需要的测试数量和错误数量是没有隐式转换的两倍。


2

显式强制转换很重要,因为它们可以使您的意图明确。首先,使用显式强制转换向正在阅读您的代码的人讲述一个故事。它们表明您是故意做的。而且,同样适用于编译器。例如,以下内容在C#中是非法的

double d = 3.1415926;
// code elided
int i = d;

强制转换会使您失去精确度,这很容易成为错误。因此,编译器拒绝编译。通过使用显式强制转换,您将告诉编译器:“嘿,我知道我在做什么。” 然后他会走:“好吧!” 并编译。


1
实际上,我不喜欢最显式的强制转换而不是隐式的强制转换。恕我直言,唯一的(X)y意思是“我认为Y可以转换为X;如果可能,请进行转换,否则请抛出异常”;如果转换成功,应该能够预测结果值将是什么*,而不必知道从类型转换的任何特殊规则y对类型X。如果要求将-1.5和1.5转换为整数,则某些语言会产生(-2,1); 一些(-2,2),一些(-1,1)和一些(-1,2)。尽管C的规则几乎不为人所知……
超级猫

1
...这种转换似乎比通过强制转换更适合通过方法执行(可惜的是.NET不包含有效的舍入和转换功能,而Java则以某种愚蠢的方式进行了重载)。
2015年

您告诉编译器“ 我想我知道我在做什么”。
gnasher729

@ gnasher729其中有些道理:)
Paul Kertscher

1

支持隐式转换/转换或有时被称为弱类型的语言将为您做出一些假设,这些假设并不总是与您预期或期望的行为相匹配。语言的设计者可能具有与您对某种类型的转换或一系列转换相同的思维过程,在这种情况下,您将永远不会看到问题。但是,如果没有,则您的程序将失败。

但是,失败可能是显而易见的,也可能不是显而易见的。由于这些隐式强制转换在运行时发生,因此您的程序将愉快地运行,并且仅在查看程序的输出或结果时才会看到问题。需要显式强制转换(某些人将其称为强类型)的语言会在程序甚至开始执行之前给您一个错误,这是解决隐式强制转换出错的更明显,更容易的问题。

前几天,我的一个朋友问他两岁的孩子20 + 20是什么,他回答2020。我告诉我的朋友,“他将成为一名Javascript程序员”。

用Javascript:

20+20
40

20+"20"
"2020"

因此,您可以看到隐式强制转换可能造成的问题,以及为什么它不能被解决。我认为的解决方法是使用显式强制转换。

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.