【问题标题】:F#, concise way to define map4 using map2 and map3F#,使用 map2 和 map3 定义 map4 的简洁方法
【发布时间】:2017-03-20 10:36:15
【问题描述】:

此问题仅供娱乐。请不要把这个问题看得太认真。

我目前正在学习F#,有兴趣看看有没有简洁的方式来定义map4,使用现有函数List.map2、List.map3、管道前进/后退、前进/后退组合等。

let map4 f a b c d = ......
map4 f [a1;a2] [b1;b2] [c1;c2] [d1;d2] 

// output: [f(a1,b1,c1,d1); f(a2,b2,c2,d2)]

我可以递归解决这个问题,或者定义一个新的操作符(见下面的 URL)

http://www.fssnip.net/9W/title/nary-Seqmap-

http://call-with-cc-en.blogspot.sg/2009/04/applicative-functors-mapping-function.html

我也可以通过结合 List.map2 和 List.map3 来解决这个问题,使用部分应用函数 f(a,b,?,?)

let map4 f a b c d =
    List.map3 (fun g y -> g y) (List.map2 f a b) c d

我可以尝试使用前向组合来缩短我上面的代码(并使其尽可能抽象/令人困惑)

let map4 f a =
    List.map2 f a >> List.map3 id;;

// Output type: f:('a -> 'b -> 'c -> 'd -> 'e) ->
// a:'a list -> ('b list -> 'c list -> 'd list -> 'e list)

我想知道是否可以通过去掉“f”和“a”来进一步缩短它,结果是:

let map4 = ...... (* Use only List.map2, List.map3, |>, |<, >>, <<, etc.*) ..........

这可能会造成不必要的混乱,但它会很酷。谢谢。


编辑:

改编TheInnerLight的回答:

let inline (<!>) f xList = List.map f xList

let inline (<*>) gList xList = List.map2 (id) gList xList

let map4 f w x y z = f <!> w <*> x <*> y <*> z
let map5 f v w x y z = f <!> v <*> w <*> x <*> y <*> z
let map6 f u v w x y z = f <!> u <*> v <*> w <*> x <*> y <*> z

【问题讨论】:

    标签: f#


    【解决方案1】:

    这对于应用程序风格的编程很有用,即使用applicative functors

    只需定义apply 函数和一些辅助操作符:

    module List =
        // val apply : f:('a -> 'b) list -> x:'a list -> 'b list
        let apply f x = List.map2 (fun f x -> f x) f x
    
        // val inline ( <!> ) : f:('a -> 'b) -> x:'a list -> 'b list
        let inline (<!>) f x = List.map f x
    
        // val inline ( <*> ) : f:('a -> 'b) list -> x:'a list -> 'b list
        let inline (<*>) f x = apply f x
    

    然后使用map和apply来定义mapN函数。

        // val map2 : f:('a -> 'b -> 'c) -> x:'a list -> y:'b list -> 'c list
        let map2 f x y = f <!> x <*> y
    
        // val map3 : f:('a -> 'b -> 'c -> 'd) -> x:'a list -> y:'b list -> z:'c list -> 'd list
        let map3 f x y z = f <!> x <*> y <*> z
    
        // val map4 : f:('a -> 'b -> 'c -> 'd -> 'e) -> x:'a list -> y:'b list -> z:'c list -> a:'d list -> 'e list
        let map4 f x y z a = f <!> x <*> y <*> z <*> a
    
        // val map8 : f:('a -> 'b -> 'c -> 'd -> 'e -> 'f -> 'g -> 'h -> 'i) -> x:'a list -> y:'b list -> z:'c list -> a:'d list -> b:'e list -> c:'f list -> d:'g list -> e:'h list -> 'i list
        let map8 f x y z a b c d e = f <!> x <*> y <*> z <*> a <*> b <*> c <*> d <*> e
    

    如您所见,您可以继续添加参数来定义任意 mapNs 到您的心愿。


    由于该问题专门询问有关使用map2map3 的问题,因此您可以以相同的方式执行此操作,尽管它不那么简洁,例如:

        let map4_2 f x y z a = List.map2 f x y <*> z <*> a
    
        let map4_3 f x y z a = List.map3 f x y z <*> a
    

    希望你能明白。


    顺便说一句,我认为值得注意的是,任何 monad 都会自动成为应用函子,因此您可以使用这种模式的类型很多,这里有一个 Async 示例。

    module Async = 
        // val map : f:('a -> 'b) -> x:Async<'a> -> Async<'b>
        let map f x = async.Bind(x, async.Return << f)
    
        // val apply : f:Async<('a -> 'b)> -> x:Async<'a> -> Async<'b>
        let apply f x = async.Bind(f, fun fe -> map fe x)
    
        // val inline ( <!> ) : f:('a -> 'b) -> x:Async<'a> -> Async<'b>
        let inline (<!>) f x = map f x
    
        // val inline ( <*> ) : f:Async<('a -> 'b)> -> x:Async<'a> -> Async<'b>
        let inline (<*>) f x = apply f x
    
        // val map4 : f:('a -> 'b -> 'c -> 'd -> 'e) -> x:Async<'a> -> y:Async<'b> -> z:Async<'c> -> a:Async<'d> -> Async<'e>
        let map4 f x y z a = f <!> x <*> y <*> z <*> a
    

    【讨论】:

    • 感谢您的帮助。它非常优雅。也许我们可以去掉“apply”函数,将其替换为:“”let inline () f xList = List.map f xList“”,第二部分替换为“”let inline () gList xList = List.map2 gList xList""
    • @CHBen 要根据map2 定义应用运算符,您需要使用let inline (&lt;*&gt;) gList xList = List.map2 (fun f x -&gt; f x) gList xList,或者,如果您希望它采用“高尔夫”形式,请使用let inline (&lt;*&gt;) gList xList = List.map2 (&lt;|) gList xList
    • 感谢您的建议。我忘了输入“(id)”。另外,我尝试运行您的代码,我希望“map2 (+) [1;2] [500;600]”会产生 [501;602] 的输出,但我认为您的代码中有一些错字和它产生了 [501;601;502;602]
    • @CHBen 谢谢你的提醒。与其说是一个错字,不如说是一个额外的复杂性:list两个 应用实例。在 Haskell 中,标准版本是我最初展示的版本,它将所有提升的函数与所有其他元素组合在一起,然后是 ZipList 版本,它是按元素应用的版本 - 你就是有兴趣。对不起,我应该检查代码和类型签名;)
    【解决方案2】:

    经过大量试验,我想出了以下“无点”风格的答案:

    // Warning: still have "Value Restriction" error
    let map4 = List.map2 >> (>>) >> (|>) ((>>) >> (|>) (List.map3 id))
    

    很遗憾,F# 编译器会产生“值限制”错误,请参阅: https://blogs.msdn.microsoft.com/mulambda/2010/05/01/finer-points-of-f-value-restriction/

    所以,我会解决:

    let map4 f = 
        f
        |> (    List.map2 >> (>>) >> (|>) ((>>) >> (|>) (List.map3 id))     )
    // val map4: f:('a->'b->'c->'d->'e) -> ('a list -> 'b list -> 'c list -> 'd list -> 'e list)
    

    【讨论】:

      猜你喜欢
      • 2011-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-27
      • 1970-01-01
      • 2018-07-29
      • 1970-01-01
      相关资源
      最近更新 更多