游戏规则
考虑一个简单的双人游戏,如下所示:偶数个硬币连续排列。轮流,每个玩家在该行的一端移除硬币。当所有硬币被拿走时,目标是拥有最高的硬币价值。
玩家一找到所有偶数硬币和所有奇数硬币的总和。如果奇数硬币的总和较高,则玩家一拿最左边的硬币;否则他就是最右边的。
玩家二有一个选择,硬币数量奇数。所以他试图从两端拿出一枚硬币,看看哪个选项让玩家1变得更糟。
问题
我基本上想要对这个程序实现多项目。我仍然是Clojure的新手,我在网上找不到任何关于多线程的好材料,可以应用到我的程序中。
代码
(ns game.core
(:gen-class))
(defn vector-from-string [s]
(drop 1 (map read-string (clojure.string/split (clojure.string/trim-newline s) #" "))))
(defn string-from-file [f]
(slurp f))
(defn sum-of-evens [v]
(def evens (vector))
(loop [v v, index 1]
(when (seq v)
(if (even? index)
(def evens (conj evens (first v))))
(recur (rest v) (inc index))))
(reduce + evens))
(defn sum-of-odds [v]
(def odds (vector))
(loop [v v, index 1]
(when (seq v)
(if (odd? index)
(def odds (conj odds (first v))))
(recur (rest v) (inc index))))
(reduce + odds))
(defn player-two [v p1score p2score]
(if (not (empty? v))
(if (> (max (sum-of-odds (drop 1 v)) (sum-of-evens (drop 1 v))) (max (sum-of-odds (drop-last v)) (sum-of-evens (drop-last v))))
(player-one (drop-last v) p1score (+ p2score(last v)))
(player-one (drop 1 v) p1score (+ p2score (first v))))
(println "p1score" p1score "p2score" p2score)))
(defn player-one [v p1score p2score]
(if (not (empty? v))
(if (> (sum-of-odds v) (sum-of-evens v))
(player-two (drop 1 v) (+ p1score (first v)) p2score)
(player-two (drop-last v) (+ p1score (last v)) p2score))
(println "p1score" p1score "p2score" p2score)))
(defn -main [& args]
(let [v (vector-from-string (string-from-file "numbers.txt")) ]
(player-one v 0 0)))
因此-main
首先运行player-one
函数,player-one
调用player-two
,它们都会一直运行到程序结束。我想以某种方式实现多线程,以加快执行这个游戏的开始硬币数量。
答案 0 :(得分:2)
您的代码目前非常简单。
一些评论希望能帮助您走向正确的方向:
def
(或defn
)内的def
(几乎)总是错误的。你在这里考虑变量赋值和可变变量。这不是Clojure的工作方式。如果你绝对必须使用你的recur中的变量,那么使用一个局部原子(也几乎总是错误的,但是def
里面defn
的错误常常错误)。
你的循环不必要地复杂化。你想在偶数或奇数指数上求和元素?使用reduce
,take-nth
和rest
:
(take-nth 2 [1 2 3])
;=> (1 3)
(take-nth 2 (rest [1 2 3 4]))
;=> (2 4)
整个事情看起来就像你一遍又一遍地编译它然后用它运行JVM。我对吗?首选方法是在REPL工作。如何访问它取决于您使用的编辑环境。那里有许多适合初学者的REPL。大猩猩REPL就是一个例子。
一旦您的代码和开发工作流程更加完善,您可能需要探索pmap
和future
等功能,以便轻松访问多线程。更高级的东西涉及一个名为core.async
的库,但这可能不是初学者的理想路线。您还可以回退到Java interop来创建线程。再一次,虽然不是很难做,但需要一些Clojure的经验。
希望有所帮助,即使它不是你问题的直接答案。
答案 1 :(得分:1)
首先让我们看看您的示例中的一些问题,在并行化此代码之前需要解决这些问题。
sum-of-evens
在函数中使用def
,这几乎总是一个错误。这似乎有你想要的效果,但它不是实现它的正确方法。 def
通常用于命名空间级别(与函数defn
s)处于同一级别。我们可以通过sum-of-evens
重构def
不依赖于无意的副作用行为:
(defn sum-of-evens [v]
(loop [v v
index 1
evens []]
(if (seq v)
(recur (rest v)
(inc index)
(if (even? index) ;; add a binding to loop, not a def
(conj evens (first v))
evens)) ;; pass unchanged value when necessary
(reduce + evens))))
但我们可以使用keep-indexed
进一步简化此功能:
(defn sum-of-evens [coll]
(->> coll
(keep-indexed (fn [i v] (when (even? (inc i))
v)))
(apply +)))
当我们对sum-of-odds
执行相同操作时,除了他们使用的条件外,我们可以看到函数几乎相同:odd?
与even?
。我们可以创建另一个带谓词函数的函数:
(defn sum-by-index-pred [f coll]
(->> coll
(keep-indexed (fn [i v] (when (f i) v)))
(apply +)))
;; using partial application and function composition
(def sum-of-evens (partial sum-by-index-pred (comp even? inc)))
(def sum-of-odds (partial sum-by-index-pred (comp odd? inc)))
查看player-one
和player-two
的实现,它们似乎是相互递归的。我不知道如何将它并行化以使其更快,因为每个回合都取决于前一回合的结果;没有什么可以并行化的。
我建议重构一下,以便在一个地方计算你的游戏规则和状态,而不是相互递归的函数。
(loop [scores (array-map :player-1 0 :player-2 0)
turns (cycle (keys scores))
vs (shuffle (range 100))]
(if (seq vs)
(let [higher-odds? (> (sum-of-odds vs) (sum-of-evens vs))
scores (if higher-odds?
(update scores (first turns) + (first vs))
(update scores (first turns) + (last vs)))
remain (if higher-odds?
(rest vs)
(butlast vs))]
(recur scores (rest turns) remain))
(prn scores)))
;; {:player-1 2624, :player-2 2326}
我不确定这是否会保留你原来的游戏逻辑,但它应该是接近的,并且它确实为两个以上的玩家推广它。尝试将:player-3 0
添加到起始scores
。