功能性Clojure或命令式Groovy更具可读性吗?
好了,现在不要作弊 不,真的,花一两分钟试试这个 “位置”是做什么的 编辑:根据cgrand的建议简化功能性Clojure或命令式Groovy更具可读性吗?,groovy,functional-programming,clojure,imperative-programming,Groovy,Functional Programming,Clojure,Imperative Programming,好了,现在不要作弊 不,真的,花一两分钟试试这个 “位置”是做什么的 编辑:根据cgrand的建议简化 (defn redux [[current next] flag] [(if flag current next) (inc next)]) (defn positions [coll] (map first (reductions redux [1 2] (map = coll (rest coll))))) 现在,这个版本怎么样 def positions(coll) { de
(defn redux [[current next] flag] [(if flag current next) (inc next)])
(defn positions [coll]
(map first (reductions redux [1 2] (map = coll (rest coll)))))
现在,这个版本怎么样
def positions(coll) {
def (current, next) = [1, 1]
def previous = coll[0]
coll.collect {
current = (it == previous) ? current : next
next++
previous = it
current
}
}
我正在学习Clojure,我很喜欢它,因为我一直喜欢函数式编程。我花了更长的时间才想出Clojure的解决方案,但我喜欢想出一个优雅的解决方案。Groovy解决方案还可以,但我发现这种命令式编程既无聊又机械。在使用Java 12年之后,我觉得自己陷入了一种陈规,使用Clojure进行函数式编程是我所需要的动力
对,开门见山。好吧,我必须诚实地说,我想知道几个月后回到Clojure代码时我是否理解它。当然我可以对它进行注释,但是我不需要注释我的Java代码来理解它
所以我的问题是:这是一个更加习惯函数式编程模式的问题吗?函数式编程大师们正在阅读这段代码,并发现理解它是轻而易举的事吗?你觉得哪个版本更容易理解
编辑:这段代码的作用是根据球员的得分计算他们的位置,同时跟踪那些被平局的球员。例如:
Pos Points
1. 36
1. 36
1. 36
4. 34
5. 32
5. 32
5. 32
8. 30
我不认为有任何内在的可读性。有你习惯的,也有你不习惯的。我可以阅读你的两个版本的代码。实际上,我可以更容易地阅读Groovy版本,尽管我不懂Groovy,因为我也花了十年时间研究C和Java,只花了一年时间研究Clojure。这并没有说明语言,只是说明了我的一些情况 同样,我读英语比读西班牙语容易,但这也不能说明这些语言的内在可读性。(就简单性和一致性而言,西班牙语实际上可能是这两种语言中“更具可读性”的语言,但我还是看不懂)。我现在正在学日语,日子很不好过,但以日语为母语的人对英语也这么说 如果你把大部分时间都花在阅读Java上,那么看起来像Java的东西当然比看不到的东西更容易阅读。在您花在研究Lispy语言和研究类似C语言上的时间一样多之前,这可能仍然是正确的 要理解一种语言,除其他事项外,您必须熟悉:
- 语法(
与[vector]
,(列表)
)名称中的连字符
- 词汇表(缩略是什么意思?如何/在哪里查找?)
- 求值规则(将函数视为对象是否有效?在大多数语言中这是一个错误。)
- 习惯用法,如
(映射优先(一些具有额外累积值的缩减集))
(use 'clojure.contrib.seq-utils) ;;'
(defn positions [coll]
(mapcat #(repeat (count %) (inc (ffirst %)))
(partition-by second (indexed coll))))
看着我一年前写的Clojure代码,我对它的糟糕程度感到震惊,但我能看懂它。(并不是说你的Clojure代码很糟糕;我读起来一点问题都没有,我也不是大师。)编辑:可能不再相关了
Clojure的那个对我来说很复杂。它包含了更多需要理解的抽象概念。这是使用高阶函数的代价,你必须知道它们的意思。因此,在一个孤立的案例中,祈使句需要较少的知识。但抽象的力量在于它们的组合方式。必须阅读和理解每个命令循环,而序列抽象允许您消除循环的复杂性并结合强大的运算
我还要进一步指出,Groovy版本在使用collect时至少有部分功能,collect实际上是一个高阶函数map。它也有一些状态
以下是我将如何编写Clojure版本:
(defn positions2 [coll]
(let [current (atom 1)
if-same #(if (= %1 %2) @current (reset! current (inc %3)))]
(map if-same (cons (first coll) coll) coll (range (count coll)))))
这与Groovy版本非常相似,因为它使用可变的“current”,但不同之处在于它没有next/prev变量,而是使用不可变的序列。正如Brian Elloquite所说,可读性不是内在的。这个版本是我对这一特定案例的偏好,似乎是坐在中间的某个地方。 < P>我同意蒂莫西:你引入了太多的抽象。我重新编写了您的代码,并以以下内容结束:
(defn positions [coll]
(reductions (fn [[_ prev-score :as prev] [_ score :as curr]]
(if (= prev-score score) prev curr))
(map vector (iterate inc 1) coll)))
关于你的代码
(defn use-prev [[a b]] (= a b))
(defn pairs [coll] (partition 2 1 coll))
(map use-prev (pairs coll))
可以简单地重构为:
(map = coll (rest coll))
我也在学习Clojure并热爱它。但是在我开发的这个阶段,Groovy版本更容易理解。不过,我喜欢Clojure的地方是阅读代码,当您最终“了解”正在发生的事情时,会有“啊哈!”的体验。我真正喜欢的是几分钟后发生的类似体验,当您意识到代码可以在不更改代码的情况下应用于其他类型的数据时。我已经记不清在Clojure中使用一些数字代码的次数了,然后,过了一会儿,我想到了如何将相同的代码用于字符串、符号、小部件
我使用的类比是关于学习颜色的。还记得你被介绍给红色的时候吗?你很快就明白了——世界上有这么多红色的东西。然后你听到了品红这个词,然后迷失了一段时间。但是,再一次,在稍微曝光之后,你理解了这个概念,并且有了一种更具体的方式来描述一种特定的颜色。你必须将这个概念内化,在头脑中掌握更多的信息,但最终你会得到更强大、更简洁的东西。Clojure一词乍一看更加复杂;虽然它可能更优雅。 OO是th
coll.groupBy{it}.inject([]){ c, n -> c + [c.size() + 1] * n.value.size() }
assert positions([1]) == [1]
assert positions([2, 1]) == [1, 2]
assert positions([2, 2, 1]) == [1, 1, 3]
assert positions([3, 2, 1]) == [1, 2, 3]
assert positions([2, 2, 2, 1]) == [1, 1, 1, 4]