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

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

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

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

友好讨论别打架:)

4346 次点击
所在节点    程序员
56 条回复
vencent
2019-05-25 11:06:42 +08:00
@dacapoday
@exonuclease
起初我也想过会不会“ len 的语义更接近于表示数据的尺寸,而非容器的容量或长度”。然而官方文档告诉我们:

func len(v Type) int

The len built-in function returns the length of v, according to its type:

Array: the number of elements in v.
Pointer to array: the number of elements in *v (even if v is nil).
Slice, or map: the number of elements in v; if v is nil, len(v) is zero.
String: the number of bytes in v.
Channel: the number of elements queued (unread) in the channel buffer;
if v is nil, len(v) is zero.

可见除了 string 之外,其余类型作为 len 函数的参数获取到的都是“容器的容量或长度”
vencent
2019-05-25 11:14:11 +08:00
赞同 @hhhsuan 的观点,要么就表示 bytes、要么就表示字符,不应该混在一起。
mooncakejs
2019-05-25 11:18:18 +08:00
没有提供 byteLen,
Nooooobycat
2019-05-25 11:19:38 +08:00
strings.Count
misaka19000
2019-05-25 11:20:30 +08:00
这个确实有点别扭
luozic
2019-05-25 11:24:50 +08:00
含义混乱
azh7138m
2019-05-25 11:28:46 +08:00
文字处理复杂的很,我猜 迭代的是 "字",长度求的是 byte,那我觉得 ok。
liulaomo
2019-05-25 11:29:25 +08:00
遍历 byte 的时候,使用 for i, b := range []byte(s) {} 即可(标准编译器对[]byte(s)做了优化,不会赋值底层字节),或者使用 for i := 0; i < len(s); i++ { b := s[i]; ...}。
遍历 rune 的时候,使用 for i, r := range s。

这么设计虽然牺牲了一点一致性,但达到了两全。
vencent
2019-05-25 11:31:32 +08:00
感谢 @passerbytiny 的指教。

> len(string) 只是个函数,压根谈不上设定。

其实我认为在一门语言中,len 函数的作用应该保持一致性,也就是说,它不能一会儿表示的是数据的内存尺寸,一会儿表示的是容器的容量或长度。这方面 Python 做的非常好,所有内置类型表现的非常一致。包括 str 类型,返回的也是字符个数而不是 bytes 个数(顺便一提,感谢指出 Java、PHP 与字符串有关的 lengh 方法含义都是字节个数。不过我其实认为这样的“传统”是不对的)。即使自定义类型也可以实现 Sized 接口(即实现__len__方法)使得对象的长度可以通过 len 函数获取。而 go 语言的 len 函数对于其他内置类型来说获取的是容器的长度(即元素的个数),而对于 string 类型获取的却是字节数。这种奇怪的不一致我认为是它的设计问题。
CRVV
2019-05-25 12:08:42 +08:00
@vencent

> 而 go 语言的 len 函数对于其他内置类型来说获取的是容器的长度(即元素的个数),而对于 string 类型获取的却是字节数。这种奇怪的不一致我认为是它的设计问题。

对所有类型,len 返回的都是
总字节数 / sizeof(一个元素)
对字符串,一个元素指的是 byte,否则 sizeof(一个元素) 不是常数
这是它一致的地方

另一个 len 不返回 code point 数量的原因是,得到字符串里 code point 个数需要把整个字符串遍历一遍,这样会慢

另一方面,code point 的个数也不一定是你想要的结果,因为在某些情况下多个 code point 才对应一个字,你会不会想让 len 返回字的个数?


至于为什么 for _, char := range "string" 是用 code point 来遍历而不用 byte 来遍历
我觉得你不会希望用 byte 来遍历字符串的
passerbytiny
2019-05-25 12:53:25 +08:00
@vencent #25 只有字符串才有字符个数,所以我不明白你说的“所有类型,包括 str 返回的都是字符个数”是什么意思。你如果要追求所有类型一致,那么只有基于 bytes 才有可能一致。

你对 func len(v Type) int 的文档的理解也不对,Array、map、String、Channel 返回的全部是元素个数,String 的单个元素是字节,不是字符。你需要知道的是,String 只是表面上看起来是一个有序字符序列,但本质上是表示一段字符的特殊数据结构。至于它的长度是字节数还是字符数,通常取决于大多数人怎么干,而大多数人选择字节数。不是不想选择字节数,而是真得好难。
passerbytiny
2019-05-25 13:04:25 +08:00
我突然发现了一个问题,怎么能迭代 String ?

这点还是 Java 做得好,String 是个单体不可变对象,完全没有集合的性质,根本不能被迭代。要想迭代,toCharArray() 得到 char[] charArray、 {一堆操作得到 String[] strArray}、或者{一堆操作得到 int[] codePointArray}。
jackmod
2019-05-25 13:10:32 +08:00
string 的内容是什么。有些东西会按某种 encode 计数,那确实是坑。
cnt2ex
2019-05-25 13:10:53 +08:00
如果我们现有字符( character )的定义,那么把字符串( string )的定义为字符的一个序列( a sequence of characters )。*自然地*,len 应该返回一个字符串含有多少个字符,而不是根据内部实现返回这个字符串占据了多大的空间。顶层的定义不应该被低层实现给限制。
SuperMild
2019-05-25 13:20:04 +08:00
@passerbytiny 作为单体对象就必须保存一些辅助信息,占用内存增加,各种转换又需要额外的内存和运算。而 Go 的设计者明显希望弄一些比较简单、相对更底层一些的数据结构,减少占用内存和运算。比如 slice 就十分精妙,模型很简单,需要携带的信息很少,但用起来也足够方便,string 也是采用类似的设计思想。
dacapoday
2019-05-25 15:33:09 +08:00
@vencent #21 你可能在字符串是数值还是容器上存在疑惑。从底层的角度,String 不过是一个元素类型为 char 的不定长数组。但是在多数语言中,对字符串采用 immutable 数据结构实现,声明的 String 类型,其行为更类似数据值而非容器引用。

go 这里“ String: the number of bytes in v.”就是表示字符串的数据有多大,而不是有几个字符。由于 go 采用的 utf-8 为变长编码,byte 的索引序与字符索引序不一致。如果期望字符的行为和传统语言(如 java)一致,应当考虑 go 中的 rune 类型(码点),以及 unicode 包进行处理。
dacapoday
2019-05-25 15:45:58 +08:00
@dacapoday #36 即使从语言设计上,题中“ len 和 for in 行为不一致”也是伪命题。len 面向底层,字符串处理则是更高层次的,对象化的数据操作。go 的应用领域多在后端,必然需要访问底层数据结构的能力; python 则是脚本语言,期望简易理解和操作。其版本 3 更是希望能回避版本 2 中糟糕的编码问题,选择全面封装字符串的编码实现。
karllynn
2019-05-25 15:56:45 +08:00
大哥,这是你不会用

[]byte, string, []rune 之间相互转换先学会
dacapoday
2019-05-25 15:58:00 +08:00
@vencent #29 归根结底,go 和 python 是两种语言,不能因为函数名都是'len'就想当然认为其语义和行为一致。
mornlight
2019-05-25 17:20:16 +08:00
「我个人觉得这种不一致的设计是非常反人类的。」

不认同。Go 里面很多设计是取舍后的结果,不同语言的设计哲学不同很正常。

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

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

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

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

© 2021 V2EX