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