使用地图时在集合中返回什么

时间:2012-04-27 19:54:34

标签: dictionary clojure reduce

我阅读了很多关于Clojure的文档(并且需要再次阅读),并在这里阅读几个关于Clojure的问题以获得语言的“感觉”。除了elisp中的一些小函数之外,我以前从未用任何Lisp语言编写过。我在Clojure中编写了我的第一个项目Euler解决方案,在进一步学习之前,我想更好地理解 map reduce

使用lambda,我最终得到以下结果(将3或5的所有倍数或1和1000之间的所有倍数相加):

(reduce + (map #(if (or (= 0 (mod %1 3)) (= 0 (mod %1 5))) %1 0) (range 1 1000)))

我把它放在一行,因为我在REPL上写了它(它提供了正确的解决方案)。

没有lambda,我写了这个:

(defn val [x] (if (or (= 0 (mod x 3)) (= 0 (mod x 5))) x 0))

然后我计算解决方案:

(reduce + (map val (range 1 1000)))

在这两种情况下,我的问题都涉及在执行 reduce 之前地图应返回的内容。在做了 map 后,我注意到我最终得到了一个如下所示的列表:(0 0 3 0 5 6 ...)

我尝试删除 val 定义末尾的'0',但后来我收到了一个由(零nil 3 nil 5 6等)组成的列表。我不知道 nil 是否存在问题。我发现无论如何我都会在做左撇子的时候总结,这样零点就不是问题了。

但是仍然:什么是明智的回归地图? (0 0 3 0 5 6 ...)或(nil nil 3 nil 5 6 ...)或(3 5 6 ...)(我将如何处理最后一个?)或其他什么?

我应该“过滤掉”零/零,如果是,如何?

我知道我在问一个基本的问题但是map / reduce显然是我将要使用的东西,所以欢迎任何帮助。

3 个答案:

答案 0 :(得分:2)

听起来你已经有了一个直观的未知的需要从缩小中分离映射关注由地图生成的数据非常自然地被reduce使用。事实上使用零是添加的身份值这一事实使这更加优雅。

  • 映射作业是生成新数据(在本例中为3 5或“忽略”)
  • 减少工作是决定要包括什么并产生最终结果。

你开始的是惯用的clojure,不再需要复杂化了, 所以下一个例子只是为了说明让地图决定包含什么的重点:

(reduce #(if-not (zero? %1) (+ %1 %2) %2) (map val (range 10)))

在这个人为的例子中,reduce函数忽略了零。在典型的现实世界代码中,如果想法过滤掉某些值那么简单,那么人们往往只使用filter函数

(reduce + (filter #(not (zero? %)) (map val (range 10))))

您也可以从过滤器开始并跳过地图:

(reduce + (filter #(or (zero? (rem % 3)) (zero? (rem % 5))) (range 10)))

答案 1 :(得分:1)

口号是清晰的。

  • 使用filter,而不是map。那你就不必选择null 你以后必须决定不采取行动的价值。
  • 命名过滤/映射功能可以提供帮助。使用let执行此操作 或letfn,而不是defn,除非您在其他地方使用此功能。

根据这一建议,我们可以......

(let [divides-by-3-or-5? (fn [n] (or (zero? (mod n 3)) (zero? (mod n 5))))]
  (reduce + (filter divides-by-3-or-5? (range 1 1000))))

你可能想暂时停在这里。

这很好看,但divides-by-3-or-5?功能在喉咙里。改变因素,我们需要一个全新的功能。那个重复的短语(zero? (mod n ...))罐子。所以...

我们想要一个函数,给定一个可能因素的列表(或其他集合) - 告诉我们它们是否适用于给定的数字。换句话说,我们想要

  • 数字集合的函数 - 可能的因素 - ......
  • 返回一个数字的函数 - 候选人 - ......
  • 告诉我们候选人是否可以被任何可能的因素整除。

一个这样的功能是

(fn [ns] (fn [n] (some (fn [x] (zero? (mod n x))) ns)))

......我们可以这样使用

(let [divides-by-any? (fn [ns] (fn [n] (some (fn [x] (zero? (mod n x))) ns)))]
  (reduce + (filter (divides-by-any? [3 5]) (range 1 1000))))

注释

  • 这个"改进"使程序变慢了一点。
  • divides-by-any?可能证明有用,可以晋升为 defn
  • 如果操作很关键,你可以考虑剥离 冗余因素。例如,[2 3 6]可以缩减为[6]
  • 如果操作确实关键,并且提供了因素 作为常量,您可以考虑使用a创建过滤器函数 回到使用or的宏。

这有点像shaggy-dog story,但它会重述您引用的问题所引发的想法。

答案 2 :(得分:0)

在您的情况下,我会使用keep代替map。它类似于map,只是它只保留非零值。