为了好玩,我还实现了一些排序算法(即bubble sort
和merge sort
),以及命令行接口,并测试了Clojure的功能(我已经对Clojure进行了一段时间的修改,但我仍然是个新手)。
代码看起来不错,但我认为代码的某些部分的样式可能需要改进(主要是merge sort
实现,它看起来非常冗长)。除此之外,我还可以使用其他一些改进,比如速度和性能,特别是在更大的列表中。
(ns experiments.core
(:gen-class)
(:require [experiments.command-line :as cl]))
(defn bubble-sort [array]
(loop [ind 0
sort-arr array
swaps? 0]
(if (= ind (dec (count array)))
(if (not= swaps? 0)
(bubble-sort sort-arr))
(if (> (nth sort-arr ind) (nth sort-arr (inc ind)))
(let
[temp-arr
(vec
(concat
(subvec sort-arr 0 ind)
[(nth sort-arr (inc ind))]
[(nth sort-arr ind)]
(subvec sort-arr (+ ind 2))))]
(do
(println temp-arr)
(recur (inc ind) temp-arr (inc swaps?))))
(recur (inc ind) sort-arr swaps?)))))
(defn merge-sort [array]
(defn split-array [arr]
(loop [orig arr final []]
(if (< (count orig) 2)
(if (= (count orig) 1)
(conj final orig)
final)
(recur
(subvec orig 2)
(conj final (subvec orig 0 2))))))
(defn sort-two [[a b]]
(loop [arr-a a
arr-b b
final []]
(if
(or
(empty? arr-a)
(empty? arr-b))
(vec
(concat
final
(if (empty? arr-a) arr-b arr-a)))
(if (> (first arr-a) (first arr-b))
(recur
arr-a
(vec (rest arr-b))
(conj final (first arr-b)))
(recur
(vec (rest arr-a))
arr-b
(conj final (first arr-a)))))))
(loop
[sort-arr
(split-array
(vec
(for [a (range (count array))] [(nth array a)])))]
(if (= (count sort-arr) 1)
(println (sort-two sort-arr))
(recur
(split-array
(loop [ind 0
temp-arr sort-arr]
(println temp-arr)
(if (= ind (count temp-arr)) temp-arr
(recur
(inc ind)
(vec
(concat
(subvec temp-arr 0 ind)
[(if (= (count (nth temp-arr ind)) 1)
(nth (nth temp-arr ind) 0)
(sort-two (nth temp-arr ind)))]
(subvec temp-arr (inc ind))))))))))))
(defn gen-random-array [length]
(loop [arr []]
(if (= (count arr) length) arr
(let [rand-val (rand-int length)]
(if (some #{rand-val} arr)
(recur arr)
(recur (conj arr rand-val)))))))
(defn init-args []
(cl/add-arg "-b" "bubble"
["Print the procedure of bubble-sorting,"
"given an arbitrary amount of arguments."]
(fn
([]
(bubble-sort
(gen-random-array 10)))
([args]
(bubble-sort
(vec
(for [a (range (count args))]
(read-string (nth args a))))))))
(cl/add-arg "-m" "merge"
["Print the procedure of merge-sorting,"
"given an arbitrary amount of arguments."]
(fn
([]
(merge-sort
(gen-random-array 10)))
([args]
(merge-sort
(vec
(for [a (range (count args))]
(read-string (nth args a))))))))
(cl/add-arg "-r" "random"
["Generate a random array."]
(fn
([args]
(println
(gen-random-array
(read-string (nth args 0))))))))
(defn -main [& args]
(init-args)
(cl/parse-arg (vec args)))
(ns experiments.command-line
(:gen-class)
(:require [clojure.string :as string]))
(def names (atom [["-h" "help"]]))
(def docs (atom [[""]]))
(def funcs (atom [(fn [] ())]))
(defn add-arg [s-name l-name arg-doc func]
(swap! names conj [s-name l-name])
(swap! docs conj arg-doc)
(swap! funcs conj func))
(defn disp-help []
(println "\nCommands: ")
(loop [a 1]
(if (= a (count @names))
(print "")
(do
(println " "
(nth (nth @names a) 0) "\b,"
(nth (nth @names a) 1) "\b:\n "
(string/join "\n "
(nth @docs a)))
(println "")
(recur (+ a 1))))))
(defn parse-arg [args]
(let [main-arg (nth args 0)
other-args (subvec args 1)]
(loop [a 0]
(if (= a (count @names))
(println "No match!")
(if
(or
(= main-arg (nth (nth @names a) 0))
(= main-arg (nth (nth @names a) 1)))
(if (= a 0)
(disp-help)
((nth @funcs a) other-args))
(recur (+ a 1)))))))
发布于 2017-02-16 12:27:20
让我们来对付bubble-sort
吧。
if
形式
(if (not= swaps? 0)
(bubble-sort sort-arr))
..。没有条件失败时发生的情况的表达式,因此返回nil
。以上这些需要..。
(if (not= swaps? 0)
(bubble-sort sort-arr)
sort-arr)
这个很管用。
接下来,每次执行交换时,都使用concat
重构整个向量。使用assoc
交换两个相邻元素的速度更快、更整洁:
(defn bubble-sort [array]
(loop [ind 0
sort-arr array
swaps? 0]
(if (= ind (dec (count array)))
(if (zero? swaps?)
array
(bubble-sort sort-arr))
(if (> (nth sort-arr ind) (nth sort-arr (inc ind)))
(let [temp-arr (assoc sort-arr
ind (sort-arr (inc ind))
(inc ind) (sort-arr ind))]
(recur (inc ind) temp-arr (inc swaps?)))
(recur (inc ind) sort-arr swaps?)))))
我还使用zero?
来清理我们以前看过的if
。
另一项改进是:
bubble-sort
函数对每一次通过向量执行递归调用。这将它限制为大约10K长的序列,这取决于JVM的配置方式。否则会导致堆栈溢出。
解决方案是将递归调用(bubble-sort sort-arr)
替换为等效的(recur 0 sort-arr 0)
。我们还需要将对array
的引用替换为对sort-arr
的引用,因为array
不再在每次传递时都被更新。所以我们替换
(if (zero? swaps?)
array
(bubble-sort sort-arr))
和..。
(if (zero? swaps?)
sort-arr
(recur 0 sort-arr 0))
这个很管用。试着..。
(-> (range 10000 0 -1) vec bubble-sort)
之前和之后。
一个根本的不匹配是,冒泡排序是通过就地更新来工作的,而clojure的核心数据结构是持久的--您不能改变它们。您从assoc
等方面得到的是原始版本的修改版本,它共享它的大部分结构。这带来了很大的性能成本。
因为您没有使用向量的持久性,所以可以使用瞬变。这大大降低了更新的成本- YMMV。为了这样做,
transient
创建临时版本。assoc
替换为assoc!
persistent!
恢复持久版本。我们得到..。
(defn bubble-sort [array]
(loop [ind 0
sort-arr (transient array)
swaps? 0]
(if (= ind (dec (count array)))
(if (zero? swaps?)
(persistent! sort-arr)
(recur 0 sort-arr 0))
(if (> (nth sort-arr ind) (nth sort-arr (inc ind)))
(let [temp-arr (assoc! sort-arr
ind (sort-arr (inc ind))
(inc ind) (sort-arr ind))]
(recur (inc ind) temp-arr (inc swaps?)))
(recur (inc ind) sort-arr swaps?)))))
虽然与核心sort
相比,运行速度仍然非常慢,但运行速度似乎要快一些。
https://codereview.stackexchange.com/questions/155413
复制相似问题