F# 函数式编程之 - 乾坤大挪移

Author: 102419@gmail.com
Created at: 2020-12-03

标题所说的 “乾坤大挪移” 是个夸张的说法,其实本文只是讲讲两种 “小挪移”。

第一种挪移

将面向对象编程中的 “对象” 换个位置,使其变成函数式编程中的函数的 "参数", 以便符合 F# 的编程风格。

能进行这种挪移的原因是: F# 虽然以函数式为主,但已经被扩充成也支持对象(虽然不是 “纯函数式” 听起来不够酷,但更实用更好用了,如果 F# 是纯函数式我反而没兴趣)。

在 F# 里,字符串是对象,它有方法:


    let hello = "hejjo".Replace("j", "l")
    

但这样操作,很不 F#, 函数式编程的感觉出不来,所以我们下面来进行挪移:


    let replace oldStr newStr (s:string) = 
      s.Replace(oldValue=oldStr, newValue=newStr)
    

这样我们得到了一个函数 replace, 它可以用 F# 的风格来操作:


    "hejjo" |> replace "j" "l"
    

特别是与其他函数配合时,就更能体现 F# 风格的优雅:


    // 例子出处: https://fsharpforfunandprofit.com/posts/partial-application/

    let replace oldStr newStr (s:string) = 
      s.Replace(oldValue=oldStr, newValue=newStr)

    let startsWith lookFor (s:string) = 
      s.StartsWith(lookFor)

    let result = 
        "hello" 
        |> replace "h" "j" 
        |> startsWith "j"

    ["the"; "quick"; "brown"; "fox"] 
        |> List.filter (startsWith "f")

    let compositeOp = replace "h" "j" >> startsWith "j"
    let result = compositeOp "hello"
    

第二种挪移

在上一篇文章 F# 函数式编程之 - 柯里化 currying 中我们说过 "加号 + 本质上是一个函数, 而 x + y 其实是 (+) x y 的语法糖", 在上面的代码里, |> 本质上也是一个函数,它的定义是:


    let (|>) x fn = fn x
    

可见,它的作用只是简单地把函数 fn 的参数移到前面,加上类以于 + 号的语法糖,就形成了类似“管道”的功能,在上面的代码中我们已经看到,功能虽然简单,效果却很不错。

F# 甚至允许我们自己定义这种运算符,比如:


    let (<<|) fn x = fn x
    

如上所示,我们自定义了一个 <<|, 它看起来作用不大,但它其实也很可爱,比如可以帮我们省略括号:


    printf "%i" 1+2     // error
    printf "%i" (1+2)   // ok, using parens
    printf "%i" <<| 1+2 // 也ok
    

F# 已经自带了 <|, 其定义与 <<| 完全一样,我在上面使用 <<| 只是为了说明 F# 允许用户自己定义运算符。

本文介绍了 F# 的两种 “小挪移”,一种是使对象变得更函数化,另一种是实现类似“管道”的功能,都是能让代码更优雅的小技巧。

←← →→