数据的形状 (2022)
(这是关于zest设计系列的一部分。请参阅此处的帖子列表。) 关于数据在编程系统中的表示的杂项思考,没有明确的论点。主要关注于应用级数据是如何表示、操作、查看、存储和传输的,而不是在单一语言中如何实现新的类型和数据结构。也不是一篇推广clojure的文章——我只是用clojure作为许多示例,因为这是我熟悉的唯一语言,它对数据表示进行了深思熟虑的考虑。表示法 let room_occupants = HashMap :: < _ , _ > :: from_iter([ ( 42 u64 , vec! [ "Alice" . to_string () , "Bob" . to_string ()]) , ( 43 u64 , vec! [ "Eve" . to_string ()]) , ]) ; println! ( " {:?} " , room_occupants) ; // {43: ["Eve"], 42: ["Alice", "Bob"]} 当我写这段rust代码时,我必须考虑:我编写的用于创建和修改数据的代码。我在内存中使用的表示,它帮助我思考代码如何执行(我脑海中某种模糊的盒子和箭头图示)。在我的日志和调试语句中出现的打印表示。尽管这三种情况下数据模型是相同的,但我必须处理三种完全不同的表示法。(更糟的是,我对于这两种表示法没有解析器。)以下是在clojure中的等效代码: user=> ( def room-occupants { 42 [ "Alice" "Bob" ] , 43 [ "Eve" ]}) #' user/room-occupants user=> room-occupants { 42 [ "Alice" "Bob" ] , 43 [ "Eve" ]} user=> ( pr-str room-occupants) "{42 [ \" Alice \" \" Bob \" ], 43 [ \" Eve \" ]}" user=> ( clojure.edn/read-string ( pr-str room-occupants)) { 42 [ "Alice" "Bob" ] , 43 [ "Eve" ]} 在clojure中,我用于表达数据结构的代码与打印表示是完全相同的。扩展数据表示法(edn)是clojure的一个子集,就像javascript对象表示法(json)是javascript的一个子集。这很不错,当我想从repl中复制一个值到我的代码中时。或者从我的日志中复制一个大的数据结构到我的repl,以便我可以以编程方式检查它,而不是来回滚动。但这只是一个小好处。更大的好处是:当我在大多数语言中编写代码时,我用来思考内存中数据的心理表示是一种模糊的盒子和箭头的东西。当我在clojure中编写代码时,我的心理表示字面上就是数据字面量语法。我认为这类似于经常评论的数学的符号机械化。当我在脑海中解决数学问题时,通常不考虑数值本身及其关系。相反,我在脑海中形象化了一些代数表达式,然后在脑海中移动这些表达式。通过将复杂问题映射为对我的心理视觉/空间能力友好的表示法,我能够解决比直接思考问题更复杂的问题。通过对数据有一个精心设计的表示法并在各处一致使用,我认为clojure正尝试为数据建模做类似的事情。他们在表示法的设计中嵌入了很多好的默认设置,教程序员一小套用于操作该表示法的原语,然后普遍使用该表示法。Javascript/json也是无意中做了类似的事情,我认为这解释了json的成功的很多原因。但javascript在表示法上并不一致,例如: var room_occupants = { 42 : [ "Alice" , "Bob" ] , 43 : [ "Eve" ] , } ; console . log (room_occupants) ; // { '42': [ 'Alice', 'Bob' ], '43': [ 'Eve' ] } // 这么接近 - 但为什么我们的键是字符串?! // javascript现在有了真正的数据结构! var room_occupants = new Map ([ [ 42 , [ 'Alice' , 'Bob' ]] , [ 43 , [ 'Eve' ]] , ]) ; console . log (room_occupants) ; // Map(2) { 42 => [ 'Alice', 'Bob' ], 43 => [ 'Eve' ] } // 不是有效的javascript语法! console . log (room_occupants . toString ()) // '[object Map]' console . log ( JSON . stringify (room_occupants)) // {} 有趣的是,rust的serde在这方面通常比javascript更好,是我进行println调试或日志记录的首选。其他地方的表示法 数据表示法无处不在——几乎每当人类参与生成、转换、检查或调试数据时,我们都需要某种表示法来与之互动。在数据库中... jamie = # insert into room_occupants values (42, 'Alice'), (42, 'Bob'), (43, 'Eve'); INSERT 0 3 jamie = # select * from room_occupants; room | occupant_name ------+--------------- 42 | Alice 42 | Bob 43 | Eve ( 3 rows) 在网络中... println! ( " {} " , serde_json :: to_string( & room_occupants) . unwrap ()) ; // {"43":["Eve"],"42":["Alice","Bob"]} 在调试器中... > rust-gdb target/debug/test-rs -ex 'catch syscall write' -ex 'run' -ex 'f 15' 捕捉点 1 (调用syscall write),0x00007ffff7f9bea3 在 __libc_write (fd=1, buf=0x5555555bf500, nbytes=36) 处
本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。
☕请我喝杯咖啡