2012-04-27 28 views
1

我读了很多关于Clojure的文档(并且需要再次阅读它),并阅读了几篇关于这个Clojure的问题以获得对该语言的“感觉”。除了elisp中的一些小函数之外,我从来没有用过任何Lisp语言编写过。我在Clojure编写了我的第一个项目Euler解决方案,在进一步深入之前,我想更好地了解一下地图减少什么在使用地图时返回一个集合

使用Lambda,我结束了以下(总结3或5或同时1和1000(含)之间的所有倍数):

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

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

没有拉姆达,我写了这个:

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

然后我计算解决方案这样做:

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

在这两种情况下,我的问题是关于地图做之前应该返回什么, 减少。在做完地图后,我注意到我最后列出了一个如下所示的列表:(0 0 3 0 5 6 ...)

我试图在VAL定义的末尾去除“0”但我接收由列表(零零零3 5 6等)。我不知道是否有问题。无论如何,我发现我会在做一个折叠的时候总结一下,所以零不是真正的问题。

但仍然:什么是一个合理的地图返回? (0 0 3 0 5 6 ...)或(无零3无5 6 ...)或(3 5 6 ...)(我将如何处理这最后一个?)或其他?

我应该“过滤”零点/零点,如果是的话如何?

我知道我在问一个基本问题,但map/reduce显然是我会用的很多,所以任何帮助都是值得欢迎的。

回答

2

这听起来像你已经有一个直觉的undetermined需要单独映射的关注形式减少有一个非常自然的地图产生的数据不被减少使用。事实上使用零是添加的身份值的事实使这更加优雅。

  • 映射工作是产生新的数据(在这种情况下,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))) 
+0

+1 ......我不知道直观:)我只是想多读文档,尽我所能,但随后的问题是,我知道我已经忘了几个事情,我读:)让我们回到映射产生的数据,如果我有一个情况,我不希望零(在这种情况下罚款),也不在输出零?在ouptut集合中“过滤”不需要的元素的常见Clojure方法是什么?只需使用* filter *? – 2012-04-27 20:43:54

+0

是的,如果发现你自己说,喔,我可以“只使用X”你可能在正确的轨道 – 2012-04-27 20:53:58

+0

@ArthurUlfeldt在何种意义上减少“决定如何处理包括”? – Thumbnail 2014-04-06 09:31:51

0

在你的情况我会使用keep代替map。它与map类似,但它只保留非零值。

+0

我认为['filter'](http://clojuredocs.org/clojure_core/clojure.core/filter)将更好地工作,因为'(或(= 0(MOD×3))(= 0(MOD×5) ))''会在'nil'失败时返回'false'。 – huon 2012-04-28 12:04:33

+0

@dbaupp切换“else”--condition返回'nil'将启用我的解决方案或从'if'切换到'when'。 – ponzao 2012-04-28 12:50:19

+1

但它不允许像'(filter#(or(= 0(mod%3))(= 0(mod%5)))...)'这样轻松地写出它。 (我想说的是'keep'有它的用途,但'filter'是适合在这里使用的函数:)) – huon 2012-04-28 12:52:41

1

的口号是清晰度。

  • 使用filter而不是map。那么你不必选择一个你以后不得不决定不采取行动的空值。
  • 命名过滤/映射函数可以提供帮助。与letletfn,不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]
  • 如果操作是真的关键,因素提供 常数,你可以考虑用 宏回到使用or创建过滤器功能。

这是有点shaggy-dog story,但它叙述了你所指的问题提示的想法。

相关问题