Clojure自定义函数内执行reduce报new-map符号无法解析错误
错误原因
你封装代码抛出符号无法解析错误的核心原因是自定义函数的参数名和Clojure内置特殊标识符冲突:
fn是Clojure内置的、用于定义匿名函数的核心特殊形式,属于保留标识符。你将函数的第一个形参命名为fn后,当前上下文里的fn会被绑定为你传入的参数,直接遮蔽Clojure核心的fn定义。- 此时
reduce里写的(fn [new-map [key val]] ...)不会被解析为「定义一个接收累积值new-map、键值对entry的匿名函数」,而是会被解析为「调用你传入的名为fn的参数,把向量[new-map [key val]]作为入参传入」。在这个解析逻辑下,new-map从来没有被声明为局部绑定,自然会抛出无法解析符号的错误。
你自己写的第二个实现可以正常运行,是因为你用#()读宏定义匿名函数,没有用到被遮蔽的fn标识符,所以不会触发解析异常。
修复方案
只要把和内置标识符冲突的参数名改掉即可,Clojure生态里习惯用f作为传入函数的参数名,修改后的通用实现如下:
(defn map-map [f hash] (reduce (fn [new-map [key val]] (assoc new-map key (f val))) {} hash))
注意你原来的封装代码里把转换逻辑写死成了inc,既然要做通用的map值映射函数,需要把inc替换为传入的转换函数f,否则函数永远只能对map值做加1操作,无法适配其他转换场景。
测试运行效果:
;; 加1转换 (map-map inc {:max 30 :min 10}) ;; => {:max 31, :min 11} ;; 转字符串转换 (map-map str {:a 1 :b 2}) ;; => {:a "1", :b "2"}
额外提示
Clojure不会强制禁止你用内置标识符作为参数或局部变量名,但这种写法非常容易引发诡异的解析错误,日常编码时尽量避开用fn、map、reduce、def、let这类核心内置标识符命名自定义变量。
内容的提问来源于stack exchange,提问作者jcubic
相关产品推荐
相关产品推荐

