Smalltalk中基于字典统计用户推文数量的代码可读性与效率问询
嘿,咱们来拆解下你的这段Smalltalk代码,聊聊它的可读性、效率,还有更顺手的实现方式!
首先,先提个小bug:你的代码里最后用了result at: user,但result变量根本没定义——应该是笔误,想写aDictionary对吧?这是要先修正的点。
现有代码的可读性问题
目前的代码是一行到底的结构,变量声明和逻辑挤在一起,刚看的时候得花点时间拆解每一步做什么。比如:
tweetCountUserFrom: tweet in: aDictionary | user | user := tweet user. aDictionary at: user ifAbsentPut: 0. aDictionary at: user put: (result at: user) + 1
这种紧凑写法在Smalltalk里虽然合法,但不符合常规的代码风格,可读性大打折扣。
现有代码的效率问题
这段代码里对同一个user键做了两次字典查找:一次是at:ifAbsentPut:,一次是at:put:。哈希表的查找虽然很快,但在处理大量推文时,重复的查找还是会累积额外的性能开销。
可读性优化版
先把代码格式化,遵循Smalltalk的命名惯例(参数用aXXX的形式),拆分逻辑步骤,让每一步都清晰:
tweetCountUserFrom: aTweet in: aDictionary | user currentCount | user := aTweet user. "如果用户不存在,默认计数为0" currentCount := aDictionary at: user ifAbsent: 0. "计数加1后存回字典" aDictionary at: user put: currentCount + 1
这样写下来,每个变量的作用、每一步的逻辑都一目了然,别人(包括未来的你)看代码时不用费脑拆解。
更高效的实现(减少字典查找)
Smalltalk的字典类提供了at:update:ifAbsentPut:方法(不同方言比如Pharo、Squeak都支持),可以把“查找-更新”合并成一次操作,只做一次哈希查找:
tweetCountUserFrom: aTweet in: aDictionary aDictionary at: aTweet user update: [ :existingCount | existingCount + 1 ] ifAbsentPut: 1
这个方法的逻辑是:找到user对应的键,如果存在就用block把现有值加1,如果不存在就直接存入初始值1——一步到位,既简洁又高效。
集合级的更优方案
如果你是要处理整个tweets集合,没必要逐个调用上面的方法,直接用inject:into:就能完成统计,代码更简洁:
tweets inject: Dictionary new into: [ :countDict :aTweet | countDict at: aTweet user update: [ :count | count + 1 ] ifAbsentPut: 1. countDict ]
这段代码从空字典开始,遍历每个推文,直接在字典里更新对应用户的计数,最后返回统计好的字典,全程只用写一次逻辑。
总结
你的核心逻辑是对的,只要修正笔误、调整代码格式,可读性就能上来;而通过at:update:ifAbsent:或者集合级的inject:,能减少不必要的字典查找,提升效率,同时让代码更简洁。
内容的提问来源于stack exchange,提问作者Emanuel

