我正在学习Clojure,我正在使用4Clojure 作为一种资源。我可以解决很多" easy"在网站上的问题,但对我来说,在函数式编程思维中思考仍然不自然(我来自Java)。因此,我在我的大多数seq构建实现中使用循环/重复迭代模式,因为这是我以前的思考方式。
然而,当我看到更有经验的Clojure用户的答案时,他们会以更实用的方式做事。例如,in a problem about implementing the range function,我的答案如下:
(fn [start limit]
(loop [x start y limit output '()]
(if (< x y)
(recur (inc x) y (conj output x))
(reverse output))))
虽然这有效,但其他用户做了这样的事情:
(fn [x y] (take (- y x) (iterate inc x)))
我的功能更冗长,我不知道&#34;迭代&#34;功能甚至存在。但是从效率的角度来看,我的回答是否更糟?循环/重复使用比替代品更糟糕吗?我担心将来会发生很多这样的事情,因为还有许多函数,比如我不知道的迭代。
答案 0 :(得分:5)
第二个变体返回一个惰性序列,这可能确实更有效,特别是如果范围很大。
另一件事是第二种解决方案更好地传达了这个想法。换句话说,它描述了意图而不是实现。与您的代码相比,它需要更少的时间来理解它,您需要通过循环体读取并在头脑中构建控制流模型。
关于新功能的发现:是的,您可能事先不知道已经定义了某些功能。在Haskell中更容易,你可以通过它的类型签名搜索函数,但是通过一些经验,你将学会识别像这样的函数式编程模式。您将编写类似第二个变体的代码,然后在标准库中查找类似take
和iterate
的内容。
答案 1 :(得分:3)
为Clojure Cheetsheet website添加书签,并始终打开浏览器标签。
研究所有功能,特别是阅读它们链接到的示例(http://clojuredocs.org网站)。
网站http://clojure-doc.org也非常有用(是的,两个名字几乎相同但不完全相同)
答案 2 :(得分:3)
问题不应该是关于性能(取决于!),而是关于沟通:当使用loop/recur
或普通递归或lazy-seq
或有时甚至是reduce
时,您的代码更难理解:因为读者必须先了解如何执行迭代才能理解你的计算内容。
答案 3 :(得分:1)
loop
/ recur
是真正的Clojure,也是惯用的。这是有原因的。通常没有更好的方法。但是很多人发现,一旦习惯了它,用iterate
之类的构建块构建许多函数是非常方便的。 Clojure有很好的收藏品。我开始使用真正的递归算法从头开始编写东西,然后循环/重复。就个人而言,我不会声称使用功能构建模块功能更好,但我喜欢使用它们。这是Clojure最棒的事情之一。
(是的,许多构建基块功能是懒惰的,例如for
和map
,这些功能更为通用。懒惰可能很好,但我并不虔诚它。有时效率更高。有时它不是。有时候它很漂亮。有时候后面是痛苦。有时甚至是这样。)
答案 4 :(得分:1)
Loop和recur也不错 - 事实上,如果你看一下许多内置函数的源代码,你会发现它们就是这样做的 - 提供的函数通常是常见模式的抽象,它们可以使您的代码更容易理解。当你第一次开始时,你是如何做事的。你如何接近这似乎对我来说是正确的。您不只是编写解决方案并继续前进。您正在编写解决方案,然后查看其他人如何解决相同问题并进行比较。这是改善的正确道路。强烈建议当您找到一个看起来更优雅/高效/清晰的替代解决方案时,分析它,查看它使用的内置函数的源代码,并且事情将慢慢融合在一起。
答案 5 :(得分:1)
我好几天都想知道同样的事情,但真正的很多时间我没有看到比循环复发更好的选择。 有些工作没有完全“减少”或“映射”。当您在每次迭代时更改的缓冲区更新数据库时就是这种情况。
Loop recur非常方便,需要“非线性精确工作”。它看起来更加迫切,但如果我记得很好,Clojure的设计是实用主义的。购买,实用主义意味着选择更有效的东西。
这就是为什么在复杂的程序中,我同时使用Clojure和java代码混合。有时java对于“低级别”或迭代作业(如获取特定值等)更加清晰,而我认为Clojure函数对于大数据处理更有用(没有太多细节:全局过滤器等)。 p>
有些人说我们必须尽可能地使用Clojure,但我认为没有任何理由不使用Java。我没有编程很多,但Clojure / Java是我见过的最好的互操作,非常互补的方法。