【问题标题】:Clojure: Complex Iteration Across a List?Clojure:跨列表的复杂迭代?
【发布时间】:2014-12-02 02:26:34
【问题描述】:

我想要一个数字、20 和一个列表。 '(1 2 3 4 5 6 7 8 9 10),并返回一个集合,其中包含原始列表中每个值的两个值:原始值与该值除以 20 时的余数配对。如果原始值以某种方式键入余数,那就太好了,这样我就可以轻松检索产生特定余数的每个数字。基本上我想要一些功能func:

user=> (func 20 '(1 2 3 4 5 6 7 8 9 10))
'(:0 1, :0 2, :2 3,... :20 0)

然而,我很难弄清楚如何遍历列表。谁能帮我理解如何独立使用列表的元素,然后如何返回 20 除以的元素以及是否返回余数?

我的想法是在计算平方根的程序中使用类似的东西。如果数字由余数键入,那么我可以查询集合以获取将输入除以余数 0 的所有数字。


这是我的初步处理方法。

;; My idea on the best way to find a square root is simple.
;; If I want to find the square root of n, divide n in half
;; Then divide our initial number (n) by all numbers in the range 0...n/2 
;; Separate out a list of results that only only return a remainder of 0.
;; Then test the results in a comparison to see if the elements of our returned 
;; list when squared are equal with the number we want to find a square root of.
;; First I'll develop a function that works with evens and then odds

(defn sqroot-range-high-end [input] (/ input 2))
(sqroot-range-high-end 36) ; 18

(defn make-sqrt-range [input] (range (sqroot-range-high-end (+ 1 input))))
(make-sqrt-range 36) ; '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18)

(defn zero-culler [input] (lazy-seq (remove zero? (make-sqrt-range input))))
(zero-culler 100) ; '(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18)

(defn odd-culler [input] (lazy-seq (remove odd? (zero-culler input))))
(odd-culler 100) ; '(2 4 6 8 10 12 14 16 18)

;;the following is where I got stuck
;;I'm new to clojure and programming,
;;and am just trying to learn in a way that I understand

(defn remainder-culler [input]
  (if
    (/ input (first odd-culler (input)))
  input)
  (recur (lazy-seq (input)))
)

(remainder-culler 100)

【问题讨论】:

  • 你不需要在lazy-seq中包装remove,它已经是惰性的了。

标签: list loops if-statement clojure square-root


【解决方案1】:
(map #(vector (rem 20 %) %) (range 1 21))
;; => ([0 1] [0 2] [2 3] ... [1 19] [0 20])

然而,我很难弄清楚如何遍历列表

要进行迭代,请使用像clojure.core/map这样的高阶函数

返回20被除以的元素,如果它返回余数

您想退回 2 件东西。提供给clojure.core/mapfn 可以通过返回一个包含两个元素的向量来实现。

要确定余数,请使用rem

键入返回的余数

因为可能有多个操作返回相同的余数,所以我们不能创建映射 ({}) 并将余数用作键 - 会发生冲突。

clojure.core/range 用于创建分母集合。

为了不那么明确并使用clojure.core/juxt,以下实现相同:

(map (juxt (partial rem 20) identity) (range 1 21))

最后,参数化整个事情并返回一个惰性序列:

(defn rem-denominator
  [n]
  (map (juxt (partial rem n) identity)
       (iterate inc 1)))

(take 5 (rem-denominator 20))
;; => ([0 1] [0 2] [2 3] [0 4] [0 5])
(take 20 (rem-denominator 20))
;; => ([0 1] [0 2] [2 3] ... [1 19] [0 20])

【讨论】:

  • 非常不错的代码,但您可能想为它写一个解释。
【解决方案2】:

欢迎使用 Clojure!

快速说明:[1 2 3 4 5 6 7 8 9 10] 是一个向量,而不是一个列表。

当你说“keyed to”时,我觉得你正在寻找能返回地图的东西。

地图

这就是Clojure's cheatsheet 真正派上用场的地方。您正在尝试从函数创建地图。地图是一种集合,因此如果您转到备忘单上的“集合”部分,然后向下滚动到地图,您会看到许多类别。您想创建一个,因此请查看该列表,并浏览 Clojure 文档的链接。

这将引导您使用非常方便的group-by 函数。你给它一个函数和一个集合,它会返回一个包含该集合中所有项目的映射,以将 f 应用于每个值的结果作为键。

> (group-by #(rem 20 %) [1 2 3 4 5 6 7 8 9 10])
{0 [1 2 4 5 10], 2 [3 6 9], 6 [7], 4 [8]}

如果您想让键成为实际的关键字,您必须修改匿名函数以返回关键字:

> (group-by #(keyword (str (rem 20 %))) [1 2 3 4 5 6 7 8 9 10])
{:0 [1 2 4 5 10], :2 [3 6 9], :6 [7], :4 [8]}

请注意,返回值是向量。这是因为您不能从一个键映射到两个项目(即映射编码函数)。

迭代/列表理解

现在,说了这么多,我不确定这就是你要找的东西。你问,“有人能帮我理解如何独立使用列表的元素,然后如何返回 20 除以的元素以及它是否返回余数?”这听起来像是for 的案例。出于您的目的,您可以将其视为迭代,但它确实在做 list comprehension

(for [i [1 2 3 4 5 6 7 8 9 10]]
    (list (rem 20 i) i))

再一次,如果你真的想使用关键字而不是值,那么你可以这样做:

(for [i [1 2 3 4 5 6 7 8 9 10]]
    (list (keyword (str (rem 20 i))) i))

在这种特殊情况下,正如 Kyle 指出的那样,您可能只使用 map:

(map #(list (keyword (str (rem 20 %)))
            %)
     [1 2 3 4 5 6 7 8 9 10])

如果您不喜欢这些返回的嵌套结构,您可以在它们上使用flatten

过滤器

但我仍然不确定您是否希望这能解决您的问题。在您的 cmets 中,您有“分离出仅返回余数 0 的结果列表”。在我看来,这听起来像是 filter 的一个案例,它的附带好处是懒惰。

> (filter #(zero? (rem 20 %)) [1 2 3 4 5 6 7 8 9 10])
(1 2 4 5 10)

达达。它只是吐出原始集合中满足您需求的元素。

希望这会有所帮助。它不会让你一直朝着你的目标前进,但我希望它能给你一些可以用来实现目标的简洁工具。你有选择!在您学习的同时,尝试多种选择。如果你在某处读到一个比另一个更可取,看看你是否能找出原因。

【讨论】:

  • 感谢您,这非常有帮助。
猜你喜欢
  • 2011-05-23
  • 1970-01-01
  • 2015-06-16
  • 2014-07-27
  • 2019-02-20
  • 2020-12-07
  • 1970-01-01
  • 2023-03-23
  • 1970-01-01
相关资源
最近更新 更多