Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.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
Loops 交叉;《时代》杂志;及;至于;功能?_Loops_Macros_Clojure - Fatal编程技术网

Loops 交叉;《时代》杂志;及;至于;功能?

Loops 交叉;《时代》杂志;及;至于;功能?,loops,macros,clojure,Loops,Macros,Clojure,我经常发现自己希望使用整数索引(如“dotimes”)多次高效地运行Clojure函数,但也希望将结果作为现成的序列/列表(如“for”)输出 i、 e.我想这样做: (fortimes [i 10] (* i i)) => (0 1 4 9 16 25 36 49 64 81) 显然可以做到: (for [i (range 10)] (* i i)) 但是如果可能的话,我想避免创建和丢弃临时范围列表 在Clojure实现这一目标的最佳方式是什么 如您在第二个示例中所示,在for循环

我经常发现自己希望使用整数索引(如“dotimes”)多次高效地运行Clojure函数,但也希望将结果作为现成的序列/列表(如“for”)输出

i、 e.我想这样做:

(fortimes [i 10] (* i i))

=> (0 1 4 9 16 25 36 49 64 81)
显然可以做到:

(for [i (range 10)] (* i i))
但是如果可能的话,我想避免创建和丢弃临时范围列表


在Clojure实现这一目标的最佳方式是什么

如您在第二个示例中所示,在for循环中生成范围是Clojure中解决此问题的惯用解决方案

由于Clojure基于函数范式,因此默认情况下,在Clojure中编程将生成这样的临时数据结构。但是,由于“range”和“for”命令都使用惰性序列操作,因此编写此代码不会强制整个临时范围数据结构同时存在于内存中。因此,如果使用得当,本例中使用的惰性seq的内存开销非常低。此外,您的示例的计算开销是适度的,并且应该只随范围的大小线性增长。这被认为是典型Clojure代码的可接受开销


如果临时范围列表对于您的情况绝对不可接受,则完全避免这种开销的适当方法是使用原子或瞬态编写代码:。但是,如果您这样做,您将放弃Clojure编程模型的许多优点,以换取稍好的性能。

我不知道您为什么要关心“创建并丢弃”由
range
函数创建的惰性序列。由
dotimes
完成的有界迭代可能更有效,它是一个内联增量,并与每个步骤进行比较,但是您可能需要支付额外的成本来表达您自己的列表连接

典型的Lisp解决方案是将新元素预先添加到一个在运行时构建的列表中,然后以破坏性方式反转该构建的列表以生成返回值。其他允许在固定时间内追加到列表的技术是众所周知的,但它们并不总是比先追加再反向的方法更有效

在Clojure中,根据函数的破坏性行为,您可以使用瞬变到达:

(let [r (transient [])]
  (dotimes [i 10]
    (conj! r (* i i))) ;; destructive
  (persistent! r))
这似乎有效,但警告不要使用
conj
到“bash values in place”-也就是说,依靠破坏性行为而不是捕获返回值。因此,这种形式需要重写

为了将上面的
r
重新绑定到每次调用
conj,我们需要使用来引入一个更高级别的间接寻址。不过,在这一点上,我们只是在对抗
dotimes
,最好使用
loop
recur
编写自己的表单

如果能够预先分配与迭代边界大小相同的向量,那就太好了。我看不出有什么办法

(defmacro fortimes [[i end] & code]
  `(let [finish# ~end]
     (loop [~i 0 results# '()]
       (if (< ~i finish#)
         (recur (inc ~i) (cons ~@code results#))
         (reverse results#)))))
给出:

(0 1 4 9 16 25 36 49 64 81)

我已经编写了一个迭代宏,它可以非常有效地执行此操作和其他类型的迭代。在github和clojars上调用包。例如:

user> (iter {for i from 0 to 10} {collect (* i i)})
(0 1 4 9 16 25 36 49 64 81 100)

这不会创建临时列表。

嗯,似乎无法回答您的评论,因为我没有注册。但是,clj iterate使用PersistentQueue,它是运行库的一部分,但不是通过读取器公开的


它基本上是一个列表,你可以把它连到最后。

谢谢约翰:-)!这是一个非常优雅的小解决方案。尽管在做相反的事情之前,它仍然会创建一个不必要的临时列表,对吗?有办法避免吗?我认为以相反的顺序执行代码可能是有意义的,尽管这可能会与任何副作用混淆….+1是一个很好的小工具!有趣的是,它是如何避免临时列表的?避免创建和丢弃额外序列的主要原因是尽量减少垃圾。是的,我知道垃圾收集现在真的很便宜,但它确实会导致延迟/GC暂停问题,这在某些应用程序中是一个真正的问题。关于这个问题的最新进展是什么?clj迭代是最好的解决方案还是有更好的替代方案?
user> (iter {for i from 0 to 10} {collect (* i i)})
(0 1 4 9 16 25 36 49 64 81 100)