这是我的问题"Recursively reverse a sequence in Clojure"的后续跟进。
是否可以使用Clojure“for”宏来反转序列?我试图更好地理解这个宏的局限性和用例。
这是我开始的代码:
((defn reverse-with-for [s]
(for [c s] c))
可能的?
如果是这样,我假设解决方案可能需要在定义可变var的某个表达式中包装for
宏,或者for宏的body-expr会以某种方式将序列传递给下一次迭代(类似到map
)。
答案 0 :(得分:7)
Clojure for
宏正在与任意Clojure序列一起使用。
这些序列可能会或可能不会像矢量那样暴露随机访问。因此,在一般情况下,您无法访问Clojure序列的最后一个元素而不会一直遍历它,这将使得以相反的顺序传递它是不可能的。
我在假设你有类似的东西(类似Java的伪代码):
for(int i = n-1; i--; i<=0){
doSomething(array[i]);
}
在这个例子中,我们事先知道数组大小n
,我们可以通过索引访问元素。使用Clojure序列我们不知道。在Java中,使用数组和ArrayLists执行此操作是有意义的。然而,Clojure序列更像链接列表 - 您有一个元素,并且引用了下一个元素。
reverse
具有该实现)。
编辑:
一般建议:不要尝试在Clojure中进行命令式编程,它不是为它而设计的。虽然许多事情可能看起来很奇怪或反直觉(与命令式编程中众所周知的习语相反),一旦你习惯了功能性的做事方式,它就会很多,而且我的意思是很多更容易。
特别针对这个问题,尽管Java(和其他类C)for
和Clojure for
同名,但它们不一样!首先是实际循环 - 它定义了流量控制。第二个是理解 - 从概念上看它是一个序列的更高功能和一个 f 的函数 它的每一个element,返回 f(element)的另一个序列。 Java for
是一个语句,它不评估任何东西,Clojure for
(以及Clojure中的任何其他东西)是一个表达式 - 它计算为 f(元素)的序列 s。
获得这个想法的最简单方法可能是使用序列函数库:http://clojure.org/sequences。此外,您可以在http://www.4clojure.com/上解决一些问题。第一个问题很容易,但随着你的进展,它们会越来越难。
*正如亚历山大的答案所示,问题的解决方案实际上是惯用的,非常聪明。为此感到荣幸! :)
答案 1 :(得分:5)
以下是使用for:#/ p>来反转字符串的方法
(defn reverse-with-for [s]
(apply str
(for [i (range (dec (count s)) -1 -1)]
(get s i))))
请注意,此代码是无变异的。它与:
相同(defn reverse-with-map [s]
(apply str
(map (partial get s) (range (dec (count s)) -1 -1))))
更简单的解决方案是:
(apply str (reverse s))
答案 2 :(得分:3)
首先,正如戈兰所说,for
不是一个陈述 - 它是一种表达,即序列理解。它通过迭代通过其他序列构建序列。所以在形式上它意味着使用它是纯粹的功能(没有副作用)。 for
可以被视为注入map
的增强型filter
。因此,它不能用于保持迭代状态,例如, reduce
做。
其次,您可以使用for
和可变状态表达序列逆转,例如使用一个原子,它是java变量的粗略等价物(不考虑其并发性)。但这样做可能会遇到几个问题:
所以不要这样做,至少在Clojure中:)
编辑:我忘记提及了。for
返回延迟序列,因此您必须以某种方式对其进行评估,以便应用您在其中执行的所有状态突变。不这样做的另一个原因:)