如何实现多线程

时间:2018-05-03 08:28:11

标签: multithreading clojure

游戏规则

考虑一个简单的双人游戏,如下所示:偶数个硬币连续排列。轮流,每个玩家在该行的一端移除硬币。当所有硬币被拿走时,目标是拥有最高的硬币价值。

玩家一找到所有偶数硬币和所有奇数硬币的总和。如果奇数硬币的总和较高,则玩家一拿最左边的硬币;否则他就是最右边的。

玩家二有一个选择,硬币数量奇数。所以他试图从两端拿出一枚硬币,看看哪个选项让玩家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,它们都会一直运行到程序结束。我想以某种方式实现多线程,以加快执行这个游戏的开始硬币数量。

2 个答案:

答案 0 :(得分:2)

您的代码目前非常简单。

一些评论希望能帮助您走向正确的方向:

def(或defn)内的def(几乎)总是错误的。你在这里考虑变量赋值和可变变量。这不是Clojure的工作方式。如果你绝对必须使用你的recur中的变量,那么使用一个局部原子(也几乎总是错误的,但是def里面defn的错误常常错误)。

你的循环不必要地复杂化。你想在偶数或奇数指数上求和元素?使用reducetake-nthrest

的组合
(take-nth 2 [1 2 3])
;=> (1 3)
(take-nth 2 (rest [1 2 3 4]))
;=> (2 4)

整个事情看起来就像你一遍又一遍地编译它然后用它运行JVM。我对吗?首选方法是在REPL工作。如何访问它取决于您使用的编辑环境。那里有许多适合初学者的REPL。大猩猩REPL就是一个例子。

一旦您的代码和开发工作流程更加完善,您可能需要探索pmapfuture等功能,以便轻松访问多线程。更高级的东西涉及一个名为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-oneplayer-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