Stream Monad的bind能否基于传入f的类型同时用作映射与过滤操作?
Stream Monad的bind操作能否同时实现映射与过滤?
模块定义代码
模块签名
module type STREAM_MONAD_SIG = sig type 'a stream val return : 'a -> 'a stream val bind : 'a stream -> ('a -> 'b stream ) -> 'b stream val (>>=) : 'a stream -> ('a -> 'b stream ) -> 'b stream val div5 : 'a stream -> 'a stream end
模块实现
module StMonad : STREAM_MONAD_SIG = struct type 'a stream = Nil | Cons of 'a * ( unit -> 'a stream) let return v = Cons(v, fun() -> Nil) let rec bind v f = match v with | Nil -> Nil | Cons(h, t) -> match f h with | Nil -> bind (t()) f | Cons(r_h, _) -> Cons(r_h, fun () -> bind (t()) f) let (>>=) = bind let div5 s1 = s1 >>= fun x -> if x mod 5 == 0 then return x else Nil let add2 s1 = s1 >>= fun x -> return (x+2) end
问题
根据传入的f的类型,该bind函数能否同时用作映射操作(如add2)和过滤操作(如div5)?
示例输入:
open StMonad;; let st = Cons(5, fun () -> Cons(6, fun () -> Cons(10, fun () -> Nil)));; let res = div5 st;;
预期输出:Cons(5, Cons(10, fun () -> Nil))
回答
这个bind函数确实可以同时支持映射和过滤操作,具体原因如下:
映射场景(如add2)
当f是fun x -> return (x+2)时,每个输入元素x都会被转换为一个包含x+2的单元素流。bind遍历原流的每个元素,将f返回的流中的元素取出并拼接成新流,完全符合映射操作“转换每个元素并保留全部元素”的语义。
过滤场景(如div5)
当f是fun x -> if x mod 5 == 0 then return x else Nil时,符合条件的元素会生成单元素流,不符合条件的则返回空流Nil。bind遇到Nil时会跳过当前元素,继续处理原流的下一个元素;遇到非空流时则保留元素,正好实现了过滤操作“只保留符合条件元素”的语义。
不过需要注意当前bind实现存在一个局限性:如果f返回的流包含多个元素,代码只会取第一个元素,忽略后续元素。严格来说,标准Monad的bind应该将f返回的整个流完整拼接到结果中,这个偏差会导致不符合Monad的结合律要求。但针对你当前的使用场景(映射返回单元素流、过滤返回单元素或空流),这个bind可以正常工作。
内容的提问来源于stack exchange,提问作者v_head
相关产品推荐
相关产品推荐

