Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Macros 在&;中收集的序列上迭代Clojure变量宏;额外参数_Macros_Clojure - Fatal编程技术网

Macros 在&;中收集的序列上迭代Clojure变量宏;额外参数

Macros 在&;中收集的序列上迭代Clojure变量宏;额外参数,macros,clojure,Macros,Clojure,问题:当要传递的参数是序列,并且catch all变量需要作为序列处理时,如何在宏中的&之后处理catch all参数?catch all变量中列出的是文字表达式 这是一个宏,旨在大致表现Common Lisp的mapc,即执行Clojure的map所做的操作,但仅用于副作用,且无惰性: (defmacro domap [f & colls] `(dotimes [i# (apply min (map count '(~@colls)))] (apply

问题:当要传递的参数是序列,并且catch all变量需要作为序列处理时,如何在宏中的
&
之后处理catch all参数?catch all变量中列出的是文字表达式

这是一个宏,旨在大致表现Common Lisp的
mapc
,即执行Clojure的
map
所做的操作,但仅用于副作用,且无惰性:

(defmacro domap [f & colls] 
      `(dotimes [i# (apply min (map count '(~@colls)))]
         (apply ~f (map #(nth % i#) '(~@colls)))))
我逐渐意识到这不是一种编写
domap
的好方法——我得到了关于这个问题的好建议。然而,我仍然对我在过程中遇到的棘手的宏观问题感到疑惑

如果集合作为文本传递,则此操作有效:

user=> (domap println [0 1 2])
0
1
2
nil
但在其他类似情况下不起作用:

user=> (domap println (range 3))
range
3
nil
user=> (def nums [0 1 2])
#'user/nums
user=> (domap println nums)
UnsupportedOperationException count not supported on this type: Symbol clojure.lang.RT.countFro (RT.java:556)
或者这个:

user=> (domap println (range 3))
range
3
nil
user=> (def nums [0 1 2])
#'user/nums
user=> (domap println nums)
UnsupportedOperationException count not supported on this type: Symbol clojure.lang.RT.countFro (RT.java:556)
问题是
colls
中的是文本表达式。这就是为什么宏
domap
在传递整数序列时起作用,而在其他情况下不起作用。请注意
'(nums)
的实例:


我尝试了
~
~
'
let
var
等多种组合,但都没有效果。尝试将其作为宏编写可能是一个错误,但我仍然很好奇如何编写一个包含这些复杂参数的可变宏。

以下是宏无法工作的原因:

”(~@colls)
此表达式创建所有coll的引用列表。例如。如果您将其传递给
(范围3)
,则此表达式将成为
”((范围3))
,因此文字参数将是您的coll之一,阻止对
(范围3)
的求值,这肯定不是您想要的

现在,如果您不在宏中引用
(~@colls)
,那么它们当然会变成像
((范围3))
这样的文本函数调用,这使得编译器在宏扩展时间之后抛出(它将尝试求值
((0 1 2))

您可以使用
list
来避免此问题:

(defmacro domap [f & colls]
  `(dotimes [i# (apply min (map count (list ~@colls)))]
     (apply ~f (map #(nth % i#) (list ~@colls)))))

=> (domap println (range 3))
0
1
2
但是有一件事很糟糕:在宏中,整个列表创建了两次。以下是我们可以避免的方法:

(defmacro domap [f & colls]
  `(let [colls# (list ~@colls)]
     (dotimes [i# (apply min (map count colls#))]
       (apply ~f (map #(nth % i#) colls#)))))
Coll并不是我们唯一需要避免多次评估的东西。如果用户将类似于
(fn[&args]…)
的内容作为
f
传递,那么lambda也将在每个步骤中编译

现在,这正是您应该问自己为什么要编写宏的场景。从本质上讲,宏必须确保所有参数都经过求值,而不以任何方式进行转换。评估免费提供函数,因此让我们将其写成函数:

(defn domap [f & colls]
  (dotimes [i (apply min (map count colls))]
    (apply f (map #(nth % i) colls)))) 
考虑到您想要实现的功能,请注意已经有一个函数可以解决这个问题,
dorun
,它只实现一个seq,但不保留
头。例如:

`(dorun (map println (range 3)))
我也会这么做

现在您已经有了
dorun
map
,您可以使用
comp
简单地组合它们来实现您的目标:

(def domap (comp dorun map))

=> (domap println (range 3) (range 10) (range 3))

0 0 0
1 1 1
2 2 2

谢谢,这正是我想要的。我没想到有必要显式调用
list
——我认为backquote是
(list…
)的语法糖。感谢关于函数多重评估的观点——我一直在想这个问题。虽然
dorun
+
map
是我想做的事情的自然方式,但我想指出的是,它并不总是最快的(请参阅我问题中的问题链接)。如果要防止对类似于此函数的函数进行多次求值,那么在
let
绑定中添加
f#f
是否可以实现这一点?是的,将f绑定到生成的符号一次可以解决此问题。然而,即使在下面的讨论中,您与A.Webb链接的答案也正确地指出,
dorun
在空间复杂度上执行O(1),因为只有头部被保存在内存中。从宏生成循环可能运行得更快,但每一步的开销应该非常非常小。与NoiseSmith基准测试相比,请基准测试(
comp-dorun-map)
我非常感谢我得到的帮助,我问题中的宏执行得非常糟糕,但是:反复被告知
dorun
+
map
是解决方案,这让人沮丧。视情况而定。这是做我想做的事情的最简单的方法,但是。。。。我一直在测试这一点,我知道这并不总是最快的<代码>(comp dorun map)
:54微秒在单个1000元素向量中迭代,使用每个元素作为向量clj向量的索引。噪声最大值
mapv
定义:20-22微秒,
doseq
19微秒,
重现:35微秒,简单
时间
19微秒。其他更糟。更正:
doseq
需要12-13微秒。(对于没有读过其他问题的人(为什么要读?),是的,我使用的是Criterium。)这很可能是因为在输入序列上调用了
seq
doseq
使用分块序列,这将在向量上工作得更快
doseq
无疑是遍历一个序列以产生副作用的最惯用方法。在向量上,它的性能应该与向量上的
dotimes
几乎相同(查找索引),这应该是最接近O(1)的。我提供了
dorun+map
,因为您要求的是
mapc
等价物
mapv
创建了一系列结果,所以我永远不会建议只在副作用时使用它。