从人性角度解决算法问题 作者: 灯小笼 时间: 2023-12-08 分类: 开发 技术可以解决大部分的问题,但是只是从技术层面上出发去考虑,往往具有较大的局限性,且容易出现吃力不讨好的局面。因为,技术最终是用来解决实际问题的,需求源自实际场景,目标也是实际场景,单纯去完成一个纯技术的动作,很容易指标不治本,缺少足够的健壮性和可扩展性。 我们有一个学校的跑步打卡群,每个月建立一个微信群,然后每个人需要设置好自己的真实姓名和目标,目标的范围是50km的整数倍,比如50、100、150,最多一般到400的样子。我们要解决的问题是,通过手段获取到每个用户的昵称后,自动解析出他的中文姓名和目标。虽然我们已经明确了,要按照中文真实姓名+目标的格式,但是具体到每个人实施的时候,就会出现各种各样的形式。比如: - 张帅-150KM - 50KM-王帅 - 陈帅@-100KM - 梁帅50km - 杨帅☆200 - 关帅~100 - 100KM-2018级孔帅 - 2019邓帅200Km ... 还会有各种千奇百怪的连接符和表现形式。从纯技术角度出发,肯定就会想到万能的正则表达式,从里边想办法辨别出中文、连接符、数字,然后过滤掉一些固定的黑名单,大概率就能从里边找出姓名和目标来。我们最开始的代码大概是这样的: ```javascript const regExp = /^(?:(\d+)(?:km[\b\-\—\~\~\d\s级]*|)([\u4e00-\u9fa5]+))|(?:([\u4e00-\u9fa5]+)(?:[\b\-\—\~\~\s级]*|\-\d+\-)(\d+)(?:km)?)$/i let targets = [] nicknames.forEach(nickname => { let ms = nickname.match(regExp) if (!ms || !ms.length) { console.error('识别错误:' + nickname) return } let target = null if (ms[1]) { target = { nickname: ms[2], target: ms[1], origin: nickname, } } else { target = { nickname: ms[3], target: ms[4], origin: nickname, } } if (target.target == 0 || target.target % 50 != 0) { console.error('识别错误:' + nickname); } else { targets.push(target) } }) ``` 实际上,每个月执行下来,总会出现几个例外,发现一些新的连接符和构成模式,于是,正则表达式就会越来越复杂。 最后,我们觉得这样下去不行了,每个月都得重新调整我们的代码,好麻烦。于是,就回过头来重新思考,人们去做这件事情时,会从人的角度去把自己的真实姓名和目标放置进去,还有一部分则还会把自己是哪一级的学生也标识进去。那么,从人的角度去理解,这些内容都有什么特点呢? 首先是年份:一般都会是`年份`或者`年份+级`,实际上,这里边最影响判断的就是,这个`级`是不是姓名的一部分,年份的话,4个数字,它和目标还是有很大的区分度的。那么,只要出现` \d{4}级` 的内容,把这部分过滤掉就行。 接下来就是找姓名,很明显,姓名肯定是中文,而中文的话,编码都是特定区间的,可以很简单的和目标以及其他无意义的分隔符区分出来。 最后就是目标了,这个还真是最简单的,因为只有可能是50的整倍。 至此,识别的算法就非常简单明了了。 ```javascript const yearExp = /\d+级/ const nameExp = /([\u4e00-\u9fa5]{2,3})/ const distanceExp = /(50|100|150|200|250|300|350|400|450|500)/ let targets = [] nicknames.forEach( nickname => { let target = { distances: 0, name: '', origin: nickname } // 去掉干扰因素 if (nickname.indexOf('级') > -1) { nickname = nickname.replace(yearExp, '') } // 匹配姓名 let ms = nickname.match(nameExp) if (ms && ms.length) { target.name = ms[1] } else { console.error(`name not found:${nickname}`) } // 匹配目标 ms = nickname.match(distanceExp) if (ms && ms.length) { target.distances = parseInt(ms[1], 10) } else { console.error(`distance not found:${nickname}`) } targets.push(target) }) ``` 这样,从人性的角度,从实际场景出发,我们做出来了非常稳定的识别算法,后面再也没有去动过这部分代码了。所以,精准地理解业务需求,才能提供更加精炼的解决方案。 标签: javascript
这种文本映射是GPT最擅长的:
这是一个跑步群的名单,每个人都会在群昵称里写上自己的名字和目标跑量,你帮我从以下文本中解析出人名和目标跑量,目标跑量一般为50的倍数,形成一个json数组{name:string,target:number}[] 张帅-150KM 50KM-王帅 陈帅@-100KM 梁帅50km 杨帅☆200 关帅~100 100KM-2018级孔帅 2019邓帅200Km ChatGPT 说: 当然,以下是从文本中解析出的JSON数组,其中包含了人名和目标跑量: json 复制代码 [ {"name": "张帅", "target": 150}, {"name": "王帅", "target": 50}, {"name": "陈帅", "target": 100}, {"name": "梁帅", "target": 50}, {"name": "杨帅", "target": 200}, {"name": "关帅", "target": 100}, {"name": "孔帅", "target": 100}, {"name": "邓帅", "target": 200} ] 在解析过程中,我忽略了文本中的一些非目标跑量的信息,并统一了目标跑量的格式。