Clojure:在向量中查找偶数

Clojure:在向量中查找偶数,clojure,Clojure,我来自Java背景,试图学习Clojure。因为最好的学习方法是编写一些代码,所以我举了一个非常简单的例子,在向量中寻找偶数。下面是我写的一段代码: ` ` 这段代码可以工作,但我不得不使用一个全局符号来让它工作,这是很蹩脚的。我必须使用全局符号的原因是,每次在向量中找到偶数时,我都想更改符号的状态。不允许我更改符号的值。有没有一种方法可以在不使用全局符号/原子的情况下实现这一点 在Clojure中,通常不需要使用loop/recur编写低级循环。这里是一个快速演示 (ns tst.clj.co

我来自Java背景,试图学习Clojure。因为最好的学习方法是编写一些代码,所以我举了一个非常简单的例子,在向量中寻找偶数。下面是我写的一段代码:

`

`


这段代码可以工作,但我不得不使用一个全局符号来让它工作,这是很蹩脚的。我必须使用全局符号的原因是,每次在向量中找到偶数时,我都想更改符号的状态。不允许我更改符号的值。有没有一种方法可以在不使用全局符号/原子的情况下实现这一点

在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正常工作:


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

(filter even? [1 2 3])
; -> (2)
出于教育目的,请使用循环/重现实现

(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
试一试

如果要返回向量或

#(filter even? %)
如果你想要一个懒惰的序列

如果您想将其与更多变换相结合,您可能需要使用传感器:

(filter even?)

如果您想使用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)))))

这将获得与内置筛选函数相同的结果。

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

现在,回答你的问题。如果您想以艰难的方式完成此操作,只需将输出作为循环的一部分:

(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))
实际上,你只是在筛选一个列表。只需使用核心过滤器:


注意,过滤器是惰性的。

甚至可以看看过滤器?和vec 退房


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

以下是一个非惰性简单解决方案,如果您始终应用相同的函数而不进行精确的工作,则可以避免循环/重现:

(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


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

你为什么要改变输出?只需将其设为循环的另一个参数。切勿在函数中使用def。除非你在做语言级的事情,否则这是不必要的。无论如何,这可以像filter=rem%20[1 2 3 4]一样解决。如果我放错了括号,很抱歉。Clojure很难在手机上写。当我回家后,我会写一个详细的答案。更简单的方法是将内部if推到一个单一的重现中:重现if even?x联合r x rxs@Thumbnail是的,那么我会在r上使用cond->。我选择以明确的风格编写示例,以使Clojure新手更容易理解。
(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 #(= (rem % 2) 0) [1 2 3 4])
(defn even-vector-2 [input](vec(filter even? input)))
(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)))