【问题标题】:Map and Reduce Monad for Clojure... What about a Juxt Monad?Clojure 的 Map 和 Reduce Monad... Juxt Monad 怎么样?
【发布时间】:2012-04-10 12:11:50
【问题描述】:

在学习 Clojure 的过程中,我花了很长时间试图理解 monad - 它们是什么以及我们如何使用它们......但没有取得太大的成功。但是,我发现了一个出色的“傻瓜单子”视频系列 - http://vimeo.com/20717301 - 由 Brian Marik 为 Clojure 制作

到目前为止,我对 monad 的理解是,它有点像宏,它允许以易于阅读的形式编写一组语句 - 但 monad 更加形式化。我的观察仅限于两个例子:

1. Identity Monad(或 'let' monad) 取自 http://onclojure.com/2009/03/05/a-monad-tutorial-for-clojure-programmers-part-1/

我们要写的形式是:

(let [a  1
      b  (inc a)]
  (* a b))

对应的monad是

(domonad identity-m
    [a  1
     b  (inc a)]
 (* a b))

2。序列单子(或“for”单子)取自http://onclojure.com/2009/03/06/a-monad-tutorial-for-clojure-programmers-part-2/

我们要写的形式是:

(for [a (range 5)
      b (range a)]
  (* a b))

对应的monad是

(domonad sequence-m
  [a (range 5)
   b (range a)]
  (* a b))

Clojure 中的 Monad 定义

看源码,使用clojure monads库-https://github.com/clojure/algo.monads

user=>(use 'clojure.algo.monads)
nil

标识单子:

user=> (source identity-m)
(defmonad identity-m
  [m-result identity
   m-bind   (fn m-result-id [mv f]
              (f mv))
  ])

序列单子:

user=> (source sequence-m)
(defmonad sequence-m
   [m-result (fn m-result-sequence [v]
               (list v))
    m-bind   (fn m-bind-sequence [mv f]
               (flatten* (map f mv)))
    m-zero   (list)
    m-plus   (fn m-plus-sequence [& mvs]
               (flatten* mvs))
    ])

所以我的结论是,monad 是某种广义的高阶函数,它接受输入函数和输入值,添加自己的控制逻辑并吐出可用于'domonad' 块。

问题 1

最后,关于问题:我想学习如何编写一个 monad,并说我想编写一个模仿 clojure 中“map”形式的“map monad”:

(domonad map-m
  [a [1 2 3 4 5]
   b [5 6 7 8 9]]
  (+ a b))

应该相当于

(map + [1 2 3 4 5] [5 6 7 8 9])

并返回值

[6 8 10 12 14]

如果我查看源代码,它应该会给我类似于 identity-m 和 sequence-m 的东西:

user=> (source map-m)
(defmonad map-m
   [m-result ...
    m-bind   ...
    m-zero   ...
    m-plus   ...
    ])

问题 2

我还希望能够定义“reduce-m”以便我可以编写:

(domonad reduce-m
  [a [1 2 3 4 5]]
  (* a))

这可能会给我 1 x 2 x 3 x 4 x 5 = 120 或

(domonad reduce-m
  [a [1 2 3 4 5]
   b [1 2 3 4 5]]
  (+ a b))

会给我 (1+2+3+4+5) + (1+2+3+4+5) = 30

终于 我是否也可以编写一个模仿 juxt 函数的“juxt monad”,但不是传递绑定值,而是传递一组函数。 :

(domonad juxt-m
  [a #(+ % 1)
   b #(* % 2)]
  '([1 2 3 4 5] b a) )

给予

[ [2 2] [4 3] [6 4] [8 5] [9 6] ] 

可能,我可以用宏来做所有这些事情,所以我真的不知道这些“单子”会有多大用处,或者它们是否甚至被认为是“单子”......有了互联网上的所有资源,它在我看来,如果我想正确学习 Monads,我必须学习 Haskell,而现在,学习另一种句法形式太难了。我想我找到了一些可能相关但对我来说太神秘的链接

请有人能解释一下!

【问题讨论】:

  • 我不知道clojure,所以我对你的实际问题没有多大帮助,但我想指出MapReduce(几乎)与map无关和reduce.
  • 我更新了帖子并取出了链接!

标签: map clojure monads reduce


【解决方案1】:

您的问题不是一种单子。它们看起来更像是一种语法糖,你可以使用宏来完成,我什至不建议你这样做,因为 map、reduce 等都是简单的函数,没有必要让它们的接口变得复杂。

这些情况不是 monad 的原因是,moand 是包装正常值的放大值。在 map 和 reduce 情况下,您使用的向量不需要被放大以使其在 map 或 reduce 中工作。

在 domoand 表达式上尝试宏扩展会对您有所帮助。 例如:序列单子示例应该扩展为:

(bind (result (range 5)) 
   (fn [a] 
      (bind (result (range a))
        (fn [b] (* a b))
   )))

其中 bind 和 result 是在序列 monad m-bind 和 m-result 中定义的函数。 所以基本上,在domand之后的向量表达式被嵌套,向量之后的表达式按原样使用,在上面的情况下,(* ab)按原样调用(只是提供了a和b值由单子)。在您的 map monad 示例中,向量表达式应该是原样,最后一个表达式 (+ a b) 应该以某种方式表示 (map + a b) 这不是 monad 应该做的。

【讨论】:

  • 对不起,如果我没有解释我的问题......我正在尝试重新发明轮子......我知道地图存在......这是最简单的例子我可以想到一些可以变成单子的东西……这就是为什么我试图通过编写“地图单子”来更多地理解单子。 'let' 和 'for' 形式也被定义为宏,但在语法上等同于 'identity monad' 和 'sequence monad'。这有点做作......但我试图更好地理解 m-bind 和 m-result 函数。
  • Map 与 monad 没有任何关系。如果您想尝试实现一些示例,请使用 try MayBe monad。
【解决方案2】:

我发现了一些非常好的 monads 资源:

所以从 Jim's Guide - http://www.clojure.net/2012/02/06/Legalities/ - 他给出了三个公理来定义“bind-m”和“reduce-m”函数:

身份 第一个单子定律可以写成

(m-bind (m-result x) f) 等于 (f x)

这意味着无论 m-result 对 x 做什么以使其成为一元值,m-bind 在将 f 应用于 x 之前都会撤消。所以关于 m-bind,m-result 有点像恒等函数。或者在范畴论术语中,它的单位。这就是为什么有时你会看到它被命名为“unit”。

反向身份第二个单子定律可以写成

(m-bind mv m-result) 等于 mv,其中 mv 是一元值。

该定律类似于第一定律的补充。它基本上确保 m-result 是一个 monadic 函数,并且无论 m-bind 对 monadic 值做什么以提取值,m-result 都会撤消以创建一个 monadic 值。

关联性 第三个单子定律可以写成

(m-bind (m-bind mv f) g) 等于 (m-bind mv (fn [x] (m-bind (f x) g))) 其中 f 和 g 是一元函数,mv 是一元值。

这条定律的意思是,不管 f 是否应用于 mv 然后 g 应用于结果,或者是否从 f 和 g 的组合中创建一个新的一元函数然后应用于MV。无论哪种顺序,结果值都是相同的一元值。换句话说,m-bind 是左右结合的。

http://www.clojure.net/2012/02/04/Sets-not-lists/ 中,他给出了一个将集合作为输入而不是列表的单子。将通过所有示例...

【讨论】:

    【解决方案3】:

    你的例子不是单子。 monad 代表可组合的计算步骤。在平凡的身份单子中,计算步骤只是一个表达式评估。

    在maybe monad中,步骤是一个可能成功或失败的表达式。

    在序列单子中,步骤是一个表达式,它产生可变数量的结果(序列的元素)。

    在 writer monad 中,计算步骤是表达式评估和日志输出的组合。在状态单子中,计算步骤涉及访问和/或修改一个可变状态。

    在所有这些情况下,monad 管道系统负责正确组合步骤。 m-result 函数封装一个“普通”值以适应一元计算方案,m-bind 函数将一个计算步骤的结果提供给下一个计算步骤。

    在 (map + a b) 中,没有要组合的计算步骤。没有秩序的概念。这只是嵌套表达式评估。减少相同。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多