我可以使用clojure'for'宏来反转字符串吗?

时间:2011-12-07 18:27:57

标签: clojure

这是我的问题"Recursively reverse a sequence in Clojure"的后续跟进。

是否可以使用Clojure“for”宏来反转序列?我试图更好地理解这个宏的局限性和用例。

这是我开始的代码:

((defn reverse-with-for [s] 
    (for [c s] c))

可能的?

如果是这样,我假设解决方案可能需要在定义可变var的某个表达式中包装for宏,或者for宏的body-expr会以某种方式将序列传递给下一次迭代(类似到map)。

3 个答案:

答案 0 :(得分:7)

Clojure for宏正在与任意Clojure序列一起使用。

这些序列可能会或可能不会像矢量那样暴露随机访问。因此,在一般情况下,您无法访问Clojure序列的最后一个元素而不会一直遍历它,这将使得以相反的顺序传递它是不可能的。

我在假设你有类似的东西(类似Java的伪代码):

for(int i = n-1; i--; i<=0){
   doSomething(array[i]);
}

在这个例子中,我们事先知道数组大小n,我们可以通过索引访问元素。使用Clojure序列我们不知道。在Java中,使用数组和ArrayLists执行此操作是有意义的。然而,Clojure序列更像链接列表 - 您有一个元素,并且引用了下一个元素。

顺便说一下,即使有一个(可能是非惯用的) *的方式,它的时间复杂度也会像O(n ^ 2)那样值得努力比较在链接的帖子中更简单的解决方案是列表的O(n ^ 2)和向量的更好的O(n)(并且它非常优雅和惯用。实际上,官方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变量的粗略等价物(不考虑其并发性)。但这样做可能会遇到几个问题:

  1. 你正在打破主要的语言范式,所以你的代码和行为代码肯定会变得更糟。
  2. 由于所有clojure可变状态单元都设计为线程安全的,因此它们都使用某种非法的并发修改保护,并且无法将其删除。因此,您将获得较差的性能特征。
  3. 在这种特殊情况下,像Goran所说,序列是广泛使用的Clojure抽象之一。例如,有懒惰的序列,可能是无限的,所以你不能走到最后。使用命令性技术尝试使用这些序列肯定会遇到困难。
  4. 所以不要这样做,至少在Clojure中:)

    编辑:我忘记提及了。 for返回延迟序列,因此您必须以某种方式对其进行评估,以便应用您在其中执行的所有状态突变。不这样做的另一个原因:)