Clojure:在向量中找到偶数

时间:2016-11-29 18:47:28

标签: clojure

我来自Java背景,试图学习Clojure。由于最好的学习方式是通过实际编写一些代码,我采用了一个非常简单的例子来查找向量中的偶数。下面是我写的代码:

`

(defn even-vector-2 [input]
  (def output [])
  (loop [x input]
    (if (not= (count x) 0)
      (do
        (if (= (mod (first x) 2) 0)
          (do
            (def output (conj output (first x)))))
        (recur (rest x)))))
  output)

`

这段代码有效,但我必须使用全局符号才能使其工作。我必须使用全局符号的原因是因为我想在每次在向量中找到偶数时改变符号的状态。 let不允许我更改符号的值。有没有办法可以在不使用全局符号/原子的情况下实现。

7 个答案:

答案 0 :(得分:6)

惯用的解决方案是直截了当的:

(filter even? [1 2 3])
; -> (2)

出于教育目的,使用loop / recur实现

(defn filter-even [v]
  (loop [r []
         [x & xs :as v] v]
    (if (seq v) ;; if current v is not empty
      (if (even? x)
        (recur (conj r x) xs) ;; bind r to r with x, bind v to rest
        (recur r xs)) ;; leave r as is
      r))) ;; terminate by not calling recur, return r

答案 1 :(得分:2)

您的代码的主要问题是您使用def污染了命名空间。你永远不应该在函数中使用def。如果您绝对需要可变性,请使用atom或类似对象。

现在,针对您的问题。如果你想以“艰难的方式”这样做,只需将output作为循环的一部分:

(defn even-vector-3 [input]
  (loop [[n & rest-input] input ; Deconstruct the head from the tail
         output []] ; Output is just looped with the input
    (if n ; n will be nil if the list is empty
        (recur rest-input
               (if (= (mod n 2) 0)
                 (conj output n)
                 output)) ; Adding nothing since the number is odd
        output)))

很少有必要使用显式循环。这是折叠的典型情况:您希望累积一个列表,该列表是另一个列表的可变长度版本。这是一个快速版本:

(defn even-vector-4 [input]
  (reduce ; Reducing the input into another list
    (fn [acc n]
      (if (= (rem n 2) 0)
        (conj acc n)
        acc))
    [] ; This is the initial accumulator.
    input))

但实际上,你只是过滤了一个列表。只需使用核心的filter

(filter #(= (rem % 2) 0) [1 2 3 4])

注意,filter是懒惰的。

答案 2 :(得分:1)

尝试

#(filterv even? %)

如果要返回矢量或

#(filter even? %)

如果你想要一个懒惰的序列。

如果你想将它与更多变换相结合,你可能想要换一个传感器:

(filter even?)

答案 3 :(得分:1)

如果你想用loop/recur写一下,我就这样做:

(defn keep-even 
  "Accepts a vector of numbers, returning a vector of the even ones."
  [input]
  (loop [result []
         unused input]
    (if (empty? unused)
      result
      (let [curr-value    (first unused)
            next-result   (if (is-even? curr-value)
                            (conj result curr-value)
                            result)
            next-unused   (rest unused) ]
        (recur next-result next-unused)))))

这与内置filter函数的结果相同。

答案 4 :(得分:0)

看看过滤器,甚至?和vec 看看http://cljs.info/cheatsheet/

(defn even-vector-2 [input](vec(filter even? input)))

答案 5 :(得分:0)

如果你想要一个懒惰的解决方案,filter就是你的朋友。

这是一个非懒惰的简单解决方案(如果在没有精确工作的情况下始终应用相同的函数,则可以避免loop/recur):

(defn keep-even-numbers
  [coll]
  (reduce
    (fn [agg nb]
      (if (zero? (rem nb 2)) (conj agg nb) agg))
    [] coll))

如果你喜欢“有趣”的可变性,这里有一个临时可变集合的解决方案:

(defn mkeep-even-numbers
  [coll]
    (persistent!
      (reduce
        (fn [agg nb]
          (if (zero? (rem nb 2)) (conj! agg nb) agg))
        (transient []) coll)))

......稍快一点!

如果将奇数/偶数定义扩展为负整数,

mod将优于rem

你也可以用你想要的集合替换[],这里是一个向量!

答案 6 :(得分:-3)

在Clojure中,您通常不需要使用*编写低级循环。这是一个快速演示。

loop/recur

结果:

(ns tst.clj.core
  (:require
    [tupelo.core :as t]  ))
(t/refer-tupelo)

(defn is-even? 
  "Returns true if x is even, otherwise false."
  [x]
  (zero? (mod x 2)))

; quick sanity checks
(spyx (is-even? 2))
(spyx (is-even? 3))

(defn keep-even 
  "Accepts a vector of numbers, returning a vector of the even ones."
  [input]
  (into []    ; forces result into vector, eagerly
    (filter is-even? input)))

; demonstrate on [0 1 2...9]
(spyx (keep-even (range 10)))

您的(is-even? 2) => true (is-even? 3) => false (keep-even (range 10)) => [0 2 4 6 8] 需要以下project.clj才能正常工作:

spyx