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

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

其一,是指将面向对象编程中的 “对象” 换个位置,使其变成函数式编程中的函数的 “参数”,以便符合 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# 的两种 “小挪移”,一种是使对象变得更函数化,另一种是实现类似“管道”的功能,都是能让代码更优雅的小技巧。

posted @ 2020-12-03 23:24  cmdOptionKana  阅读(260)  评论(0)    收藏  举报