Arity 异常迭代和过滤列表
Arity exception iterating and filtering list
我正在尝试迭代列表并过滤掉所有符合特定条件的元素。到目前为止,我使用 map 函数,并且在每次迭代时,我都会检查该元素是否应该被过滤。
(defn stock-list [book-list cat-list]
(map (fn [code] (filter #(println code) book-list)) cat-list)
)
问题是我无法在过滤器的匿名函数中使用地图匿名函数的变量“代码”,因为我得到以下 arity 异常:
Error printing return value (ArityException) at clojure.lang.AFn/throwArity (AFn.java:429).
Wrong number of args (1) passed to: github-excercises.git-excercises/stock-list/fn--5659/fn--5660
如何在匿名函数中访问外部变量?
这段代码说明了问题和解决方案:
; throws exception: Wrong number of args (1) passed to: tst.demo.core/fn--22359/fn--22360
; (mapv #(println "hello") [1 2 3])
; works
(mapv #(println "hello" %) [1 2 3]) ; not 'map' since it is lazy
; result =>
; hello 1
; hello 2
; hello 3
您的匿名函数 #(println "hello")
不接受任何参数。如果您尝试使用 arg 调用它,它将失败,如下所示:
(defn yo! [] "Yo!")
(yo!) ;=> "Yo!" as expected
(yo! 42) ; try to call it with an arg
; => clojure.lang.ArityException: Wrong number of args (1) passed to:
; tst.demo.core/fn--22401/yo!--22406
当使用 map
或 mapv
时,它会为集合中的每个元素调用一次您的函数,因此您会得到与 (yo! 42)
.
相同的异常
旁注:
大多数人建议您不要嵌套多个匿名函数(您的问题有 2 个匿名 fns),无论它们是像 (fn ...)
还是使用 reader 宏 #(...)
定义的( reader 宏被转换为 fn
形式)。
无论哪种方式,它都太难读了,而且错误信息也难以辨认(正如你刚刚遇到的!)
一个对匿名函数有用的技巧是像这样“标记”它们:
(let [myfn (fn my-secret-fn ; <= add a function "label" before the arglist
[x]
(println :result (/ 1 x)))]
(myfn 2) ; => ":result 1/2"
(myfn 0) ; => throws an exception
)
异常如下所示:
ERROR in (dotest-line-9) (Numbers.java:188)
Uncaught exception, not in assertion.
expected: nil
actual: java.lang.ArithmeticException: Divide by zero
at clojure.lang.Numbers.divide (Numbers.java:188)
clojure.lang.Numbers.divide (Numbers.java:3877)
tst.demo.core$fn__22852$my_secret_fn__22859.invoke (core.cljc:26)
tst.demo.core$fn__22852.invokeStatic (core.cljc:28)
<snip>
您可以看到堆栈跟踪中嵌入的函数标签 my_secret_fn:
tst.demo.core$fn__22852$my_secret_fn__22859.invoke
堆栈跟踪还有助于在我的编辑器中包含 my_secret_fn
(core.cljc
的 line 26
)中的错误行号。
你得到 ArityException
是因为 filter
函数的第一个参数需要一个单一参数的函数。例如,
(filter #(println 123) [1 2 3 4])
会抛出 ArityException
,但是
(filter #(println %) [1 2 3 4])
不会。
如果您只是想过滤掉(又名 remove
)序列中与特定谓词匹配的元素,请使用 remove
,如下例所示,它会删除所有正值来自序列。
user> (remove pos? [1 -1 -2 -3 4 -1 5])
(-1 -2 -3 -1)
我正在尝试迭代列表并过滤掉所有符合特定条件的元素。到目前为止,我使用 map 函数,并且在每次迭代时,我都会检查该元素是否应该被过滤。
(defn stock-list [book-list cat-list]
(map (fn [code] (filter #(println code) book-list)) cat-list)
)
问题是我无法在过滤器的匿名函数中使用地图匿名函数的变量“代码”,因为我得到以下 arity 异常:
Error printing return value (ArityException) at clojure.lang.AFn/throwArity (AFn.java:429).
Wrong number of args (1) passed to: github-excercises.git-excercises/stock-list/fn--5659/fn--5660
如何在匿名函数中访问外部变量?
这段代码说明了问题和解决方案:
; throws exception: Wrong number of args (1) passed to: tst.demo.core/fn--22359/fn--22360
; (mapv #(println "hello") [1 2 3])
; works
(mapv #(println "hello" %) [1 2 3]) ; not 'map' since it is lazy
; result =>
; hello 1
; hello 2
; hello 3
您的匿名函数 #(println "hello")
不接受任何参数。如果您尝试使用 arg 调用它,它将失败,如下所示:
(defn yo! [] "Yo!")
(yo!) ;=> "Yo!" as expected
(yo! 42) ; try to call it with an arg
; => clojure.lang.ArityException: Wrong number of args (1) passed to:
; tst.demo.core/fn--22401/yo!--22406
当使用 map
或 mapv
时,它会为集合中的每个元素调用一次您的函数,因此您会得到与 (yo! 42)
.
旁注:
大多数人建议您不要嵌套多个匿名函数(您的问题有 2 个匿名 fns),无论它们是像 (fn ...)
还是使用 reader 宏 #(...)
定义的( reader 宏被转换为 fn
形式)。
无论哪种方式,它都太难读了,而且错误信息也难以辨认(正如你刚刚遇到的!)
一个对匿名函数有用的技巧是像这样“标记”它们:
(let [myfn (fn my-secret-fn ; <= add a function "label" before the arglist
[x]
(println :result (/ 1 x)))]
(myfn 2) ; => ":result 1/2"
(myfn 0) ; => throws an exception
)
异常如下所示:
ERROR in (dotest-line-9) (Numbers.java:188)
Uncaught exception, not in assertion.
expected: nil
actual: java.lang.ArithmeticException: Divide by zero
at clojure.lang.Numbers.divide (Numbers.java:188)
clojure.lang.Numbers.divide (Numbers.java:3877)
tst.demo.core$fn__22852$my_secret_fn__22859.invoke (core.cljc:26)
tst.demo.core$fn__22852.invokeStatic (core.cljc:28)
<snip>
您可以看到堆栈跟踪中嵌入的函数标签 my_secret_fn:
tst.demo.core$fn__22852$my_secret_fn__22859.invoke
堆栈跟踪还有助于在我的编辑器中包含 my_secret_fn
(core.cljc
的 line 26
)中的错误行号。
你得到 ArityException
是因为 filter
函数的第一个参数需要一个单一参数的函数。例如,
(filter #(println 123) [1 2 3 4])
会抛出 ArityException
,但是
(filter #(println %) [1 2 3 4])
不会。
如果您只是想过滤掉(又名 remove
)序列中与特定谓词匹配的元素,请使用 remove
,如下例所示,它会删除所有正值来自序列。
user> (remove pos? [1 -1 -2 -3 4 -1 5])
(-1 -2 -3 -1)