【发布时间】:2017-02-16 05:48:41
【问题描述】:
我正在学习 Clojure,为了更好地掌握我的进步,我决定开始用该语言解决 Project Euler 问题(其中一些我已经用 C++ 和 Python 解决了)。问题1如下:
如果我们列出所有小于 10 且是 3 的倍数的自然数,或者 5,我们得到3、5、6和9。这些倍数之和是23。
求 1000 以下所有 3 或 5 的倍数之和。
这是我第一次使用 Clojure 解决方案:
(defn main1
([n]
(reduce +
(filter
#(or
(= 0 (mod % 3))
(= 0 (mod % 5)))
(range n)))))
然后我查看了我的Python版本的代码,如下:
import sys
import operator
from functools import reduce
def main(n=1000):
"""
returns solution up to but excluding n
"""
genexp = (num for num in range(1, n) if ((num % 3 == 0) or (num % 5 == 0)))
total = reduce(operator.add, genexp)
return total
if __name__ == "__main__":
if len(sys.argv) > 1:
print(main(int(sys.argv[1])))
else:
print(main())
忽略我在 Python 版本中添加的额外 CLI-args 内容,唯一的主要区别是我在 Clojure 中使用了 filter 而不是生成器。我想我也可以在 Python 中使用filter,但只是为了论证,我让我的 Clojure 代码更类似于 Python 代码:
(defn main2
([n]
(reduce + (for [i (range n)
:let [div3 (= 0 (mod i 3))
div5 (= 0 (mod i 5))]
:when (or div3 div5)]
i))))
这让我开始思考 - 我如何对这些函数进行基准测试以比较它们?对于 Python,这很容易:
$ time python python/p0001.py 10000000
23333331666668
real 0m2.693s
user 0m2.660s
sys 0m0.018s
$ time python python/p0001.py 100000000
2333333316666668
real 0m26.494s
user 0m26.381s
sys 0m0.050s
在合理的时间内(不到 30 秒)上升到 n=100,000,000。如何为我的 Clojure 函数运行类似的测试?我想我必须先编译 Clojure 代码,然后再运行它。考虑到这里的 Python 代码不是 JIT 编译的,这是否是一个公平的比较?
另一方面,我的 Clojure 代码(两个版本)的惯用程度如何?对于代码样式有什么好的建议?是否有类似 pep8 的风格指南?或者甚至类似于 Clojure 版本的 pep20:“Python 之禅”?
【问题讨论】:
-
“这样比较公平吗?”不,语言之间没有公平的比较。所有的基准在某种程度上都有缺陷。你能做的最好的事情就是确定你认为什么对语言很重要,并尝试衡量它。你想测量编译+执行时间吗?您想测量堆使用情况吗?您想比较使用每种语言的初级工程师的平均工资吗?
-
关于您的“我的 clojure 代码有多惯用”? (1) 除非你有一个多参数函数,否则将
(放在参数向量周围并不常见([n] ...(2)(= 0 ...通常表示为(zero? ...(3) 你的缩进大部分是正确的,但是搜索 clojure 风格指南,你会得到一些很好的信息。 (4) 当用许多零表示数字时,考虑使用科学计数法。 clojure 中的100,000,000也是1e8。总而言之,干得好!我会支持有关标准的建议。我几乎每天都使用它。 100,000,000 版本在这里显示的时间为 1.84 秒,仅供参考。 -
仅供参考:这对我来说运行大约 1.1 秒:
(let [n 100000000] (transduce (filter #(or (zero? (rem % 3)) (zero? (rem % 5)))) + 0 (range n)))它使用传感器来避免建立任何集合。 -
@DietrichEpp 我想我正在寻找两种不同语言中大致相似的代码之间的天真执行时间比较。在我的 Clojure 研究的早期阶段,我更关心时间复杂度而不是空间复杂度,但我认为后者也很重要(最终)。但我很理解你的观点。我被 Clojure 所吸引是因为它易于并发 - 我想到了
pmap- 在 Python 和 Clojure 中编写“天真相似”的并发代码几乎是不可能的,因为 Python 缺乏对并发性的关注。跨度> -
@Josh 感谢您的提示!实际上,我将它设置为一个多参数函数(在我的 Python 代码中将默认值设置为 1000),但是当我删除默认值调用时,我忘记了删除括号。使用
zero?和科学记数法的技巧很有帮助,而github.com/bbatsov/clojure-style-guide 似乎是我很快就会尝试了解的风格指南。
标签: python python-3.x clojure