Racket中如何创建通用函数简化重复代码实现逻辑复用
Racket 公共函数重构方案
核心结论
不需要在公共函数里写if条件分支判断返回值/元素类型,你当前的代码存在语法错误、逻辑耦合问题,用高阶函数传参的方式就能优雅复用逻辑,完全不需要硬编码类型判断。
现有代码的问题
先修正你写的代码里的基础错误:
- 重复定义了同名函数
matching-x-posn、string-with-length,Racket 默认不支持同作用域下同名函数重复定义 find-first-match里变量名写错,cons?判断的变量是lop但函数参数名是lox- 递归调用错误,公共函数递归时调用回了业务函数
matching-x-posn,没有自递归 - 自定义的
posn-x/string-length函数不存在,Racket 没有自动识别类型调用对应取值函数的能力 - 测试用例
check-expect的括号嵌套错误,多套了一层括号把参数包裹成了函数调用
重构思路
两个业务函数的核心逻辑完全一致,都是线性查找列表中第一个满足「元素提取出的比较值等于期望值」的元素,空列表时返回默认值,二者只有两个差异点:
- 从元素中提取比较值的规则:posn 取x坐标用
posn-x,字符串取长度用string-length - 空列表的默认返回值:posn 场景由调用方传入,字符串场景固定为
"no such string"
把这两个差异点作为参数传入公共函数,公共函数只保留通用的遍历、比较、递归逻辑即可,不需要感知处理的是什么数据类型。
正确实现代码
公共高阶查找函数
; 参数说明:待查找列表、目标匹配值、从元素提取比较值的函数、空列表默认返回值 (define (find-first-match lst desired get-key default) (cond [(empty? lst) default] [(cons? lst) (if (= (get-key (first lst)) desired) (first lst) (find-first-match (rest lst) desired get-key default))]))
两个业务函数的精简实现
直接调用公共函数,传入对应差异参数即可,不需要写任何重复逻辑:
; 匹配指定x坐标的posn (define (matching-x-posn lop desired-x default) (find-first-match lop desired-x posn-x default)) ; 匹配指定长度的字符串 (define (string-with-length los desired-length) (find-first-match los desired-length string-length "no such string"))
可直接通过的测试用例
(check-expect (matching-x-posn '() 10 (make-posn 0 0)) (make-posn 0 0)) (check-expect (matching-x-posn (cons (make-posn 1 2) (cons (make-posn 3 4) '())) 3 (make-posn 5 6)) (make-posn 3 4)) (check-expect (string-with-length '() 10) "no such string") (check-expect (string-with-length (cons "hi" (cons "hello" (cons "aloha" '()))) 5) "hello")
额外说明
- 不要在公共函数里硬编码类型判断分支(比如判断元素是posn还是字符串走不同逻辑),否则后续新增查找场景(比如找指定id的用户结构体)时必须修改公共函数代码,耦合度极高。
- 用传函数参数的高阶函数写法,后续新增场景只需要一行调用代码,不需要修改公共逻辑,扩展性更强。
内容的提问来源于stack exchange,提问作者Joezzz
相关产品推荐
相关产品推荐

