你们能接受一个程序设计语言里面一个内置类型对象的长度和它能迭代的次数不一样这种事情吗?

2019-05-24 23:14:47 +08:00
 vencent

最近在学一门新语言。此语言近年来越来越受欢迎(为了不引战,就不说具体是啥了,大家应该也能猜到),它的确提出了一种有意思的并发编程模型,但我个人认为在语言(语法)设计上很多地方颇为糟糕,其他人也有评论“看着很美好,写起来全是坑”。在这些“坑”中,一致性差是我认为比较难接受的。举例来说:它的 len(string) 为 11,但在 for in 循环中,string 只能迭代 5 次。

因为本人经验不算丰富,想问问大家能接受这么奇怪的设定吗,或者其他语言中也有这么奇怪的事情吗?我个人觉得这种不一致的设计是非常反人类的。

友好讨论别打架:)

4202 次点击
所在节点    程序员
56 条回复
wutiantong
2019-05-24 23:22:51 +08:00
猜不到,就你所说的这个“不一致”不是什么大问题。
这块主要是字符编码的锅吧?跟语言没多大关系的。
vencent
2019-05-24 23:32:29 +08:00
我认为:
如果一个字符串是 byte 类型,那调用 len 函数获取到的就应该是有多少个 byte,for in 循环应该是对 byte 数组循环;
如果一个字符串是字符类型,那调用 len 函数获取到的就应该是有多少个字符,for in 循环应该是对字符数组循环。

这种调用 len 函数返回 byte 个数,for in 循环又变成对字符数组进行循环的怪异行为,就非常超出人的正常思维。

好吧,我说的是 go 语言。
SuperMild
2019-05-24 23:33:18 +08:00
你可能用过的语言很少吧,对数字的处理、对字符的处理是每学一种新语言都要特别留意的地方,最容易有差异的地方。

Go 相对来说稍稍更贴近底层多一些,所以处理字符会给你暴露更多细节,让你有更多机会去做特殊操作。

另外,Go 官方文档对此有很清晰的、深入浅出的讲解。
zjyl1994
2019-05-24 23:37:35 +08:00
go 的话,转成 rune 数组就好了
wutiantong
2019-05-24 23:38:32 +08:00
@vencent 问题在于你为啥非要把 len() 跟 for-in 绑定在一起,凑成一对儿呢? 我也不是很懂你的这个概念。
wutiantong
2019-05-24 23:41:06 +08:00
@vencent 可能因为我没学过 go,说的有点想当然;我意思是,如果 for-in 与 len() 是不一致的,那应该能有一个别的函数可以跟 for-in 匹配在一起?
vencent
2019-05-24 23:44:25 +08:00
我认为正常思路来说 len 代表了一个对象的长度,也就是“我能对它迭代多少次”,而 for-in 则是迭代的过程。这两个为什么会有不一样的表现?

我充分了解 rune 类型的存在,但是我想吐槽的其实是语言设计上的问题。既然你都有 rune 类型用来表示字符数组了,为什么 string 还会有这么怪异的表现...
yvescheung
2019-05-24 23:53:59 +08:00
纯英文字符串不会出现这种问题,因为都是一个字节对应一个 rune,中英文或者欧洲语系的字符混合解码是所有编程语言都会遇到的问题,python 中这种坑更多
vencent
2019-05-25 00:06:11 +08:00
@yvescheung 能具体讲讲为什么 python 中这种坑更多吗?我主力语言是 python 但是没有感受到这种坑
KKKKKK
2019-05-25 00:11:29 +08:00
Rust 也有这个问题,string.chars()会以 Utf 编码,所以,长度和直接 len 不一致
KKKKKK
2019-05-25 00:11:46 +08:00
不过这是个好的设计
Mohanson
2019-05-25 01:23:18 +08:00
我玩过的几乎所有现代语言都是这么设计的… 如果一个字符串包含一个中文字, 众所周知它长度是 2,你 for in 的时候,它返回两个 byte 给你,你又要来这里吐槽了
gamexg
2019-05-25 02:26:02 +08:00
golang string 内部是一个 []byte,编码是 utf-8。len 获取的是 []byte 的尺寸,for in 获取的是 rune 。

原因猜测是:
如果希望 len 获取 rune 长度,由于字符串是 utf-8 编码,意味着需要遍历整个字符串才能计算出来长度,这并不是个好主意。
for in 给出 rune 的原因应该是 golang 设计之初就意图解决编码问题,全局默认 utf-8 编码,尽量隐藏编码操作。for in 部分为了不让开发者手工处理编码,那么只能直接给出 rune 类型了。

对于 len,我不希望为了获取 rune 长度而遍历整个字符串。

对于 for in 我其实是可以接受 返回 byte。 但是如果真的返回的是 byte,那么很有可能又会出现 python 里见过的问题,老外开发的一些库未考虑 中文编码,涉及中文操作就会挂,需要奇淫技巧来搞定这个问题。
hakono
2019-05-25 02:32:32 +08:00
不过是获取长度的方法和别的语言不一样罢了这都能吐槽。。。
go 的话要获取 Unicode 字符数字使用
utf8.RuneCountInString(str1)
或者
len( []rune(str1) )

丑是丑了点但你都 for range 把 unicode 字符一个个取出来了,一搬也不会有统计有几个 Unicode 的需求了。。。
dacapoday
2019-05-25 02:33:01 +08:00
len 的语义更接近于表示数据的尺寸,而非容器的容量或长度。迭代则以容器的容量为单位。
exonuclease
2019-05-25 02:41:51 +08:00
len 看起来是一个比较通用的函数 它应该是直接计算占了多少内存的 字符串会有专门的计算长度用的函数吧
hhhsuan
2019-05-25 09:24:12 +08:00
把 string 和 string.bytes 分开就合理了,go 的设计确实有问题。
len(string) == utf8 字符个数
for c in string == 遍历 utf8 字符
len(string.btyes) == byte 个数
for b in string.bytes == 遍历 bytes
passerbytiny
2019-05-25 09:38:44 +08:00
@Mohanson #11 错。Java 从 90 年代起,一个中文字的长度就是 1。对于同样采用 UTF -16 编码的 Javascript 来说,中文字的长度也是 1。对于采用 UTF-8 的语言——例如 PHP,中文字的长度是 3,但它们一般会额外提供获取字符长度的函数。你所谓的众所周知,只存在于仍然可以采用(并且人为强制使用) GB2312 或 GBK 编码的古老语言或古老开发方法中。

@vencent
你说的这个,压根就不算语言设计问题,因为 for in 循环才是语言设计,len(string) 只是个函数,压根谈不上设定。而你对 len(string)的理解有偏差,Java、PHP、Oracle ( varchar),与字符串有关的 lengh 方法,含义都是字节个数,而不是字符个数,go 维持传统是没错的。之所以有坑,是因为没有额外提供像 Java 的 String.codePointCount() 、PHP 的 mb_strlen()、Oracle 的 nvarchar 这样的补偿机制。

不过我看你的举例,貌似 Go 的语言编码不是 UTF-16,这就真是语言设计问题了。

顺便最后说一下,Java 中,只有在其中没有 Unicode SMP 的字符的时候,String.length() 才等于字符个数,否则也是不等于的。而该机制是从 Java 1.5 就开始的,是很早之前的事。以后再碰到说 Java 由于采用 Unicode 所以所有单个字符的长度都是 1 的人,你应该知道他学艺不精。
Death
2019-05-25 10:10:40 +08:00
或许可以这么理解,len 是面向底层的,for in 是面向使用者的
polebug
2019-05-25 10:35:37 +08:00
这有什么关系呢 一门语言的设计 理解它为什么这样设计不就好了吗 by the way 没有继承之类的设计 你接受了吗

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/567457

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX