功能思维。 第6部分

我们将继续介绍有关F#中的函数式编程的系列文章。 今天,我们讨论功能的关联性和组成,以及比较组成和管道。 看猫下!




关联性和功能组成


功能关联


假设有一行函数编写在一起。 它们将以什么顺序组合?


例如,此功能是什么意思?


let F xyz = xyz 

这是否意味着将函数y应用于参数z ,然后将结果传递给x ? 即::


 let F xyz = x (yz) 

还是将函数x应用于自变量y ,之后将使用自变量z评估作为结果获得的函数? 即::


 let F xyz = (xy) z 

  1. 第二个选项是正确的。
  2. 函数的使用具有关联性
  3. xyz含义与(xy) z相同。
  4. wxyz等于((wx) y) z
  5. 这看起来不妙。
  6. 我们已经看到了部分应用程序是如何工作的。
  7. 如果我们将x作为具有两个参数的函数进行讨论,则(xy) z是部分应用第一个参数的结果,然后将参数z传递给中间函数。

如果需要正确的关联性,则可以使用括号或竖线。 以下三个条目是等效的:


 let F xyz = x (yz) let F xyz = yz |> x //    let F xyz = x <| yz //    

作为练习,尝试在不进行实际计算的情况下显示这些功能的签名。


功能组成


我们多次提到函数的组成,但是这个术语的真正含义是什么? 乍一看似乎令人恐惧,但实际上,一切都很简单。


假设我们有一个函数“ f”,它将类型“ T1”映射到类型“ T2”。 我们还有一个函数“ g”将类型“ T2”转换为类型“ T3”。 然后,我们可以连接“ f”的输出和“ g”的输入,创建一个新函数,将类型“ T1”转换为类型“ T3”。



例如:


 let f (x:int) = float x * 3.0 // f  -  int->float let g (x:float) = x > 4.0 // g  -  float->bool 

我们可以创建一个新函数“ h”,该函数将“ f”的输出用作“ g”的输入。


 let h (x:int) = let y = f(x) g(y) //    g 

更加紧凑:


 let h (x:int) = g ( f(x) ) // h    int->bool // h 1 h 2 

到目前为止,如此简单。 这很有趣,我们可以定义一个新的函数“ compose”,该函数将函数“ f”和“ g”合并在一起,甚至不知道它们的签名。


 let compose fgx = g ( f(x) ) 

执行之后,您可以看到编译器正确地确定“ f ”是泛型类型'a到泛型类型'b的函数,并且' g '限于类型'b输入:


 val compose : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c 

(请注意,仅由于每个函数只有一个输入参数和一个输出,才有可能对操作进行广义组合。在非函数语言中,这种方法是不可能的。)


如我们所见,该定义用于“ >> ”运算符。


 let (>>) fgx = g ( f(x) ) 

由于有了这个定义,可以使用合成在现有功能的基础上构建新功能。


 let add1 x = x + 1 let times2 x = x * 2 let add1Times2 x = (>>) add1 times2 x // add1Times2 3 

显式记录非常麻烦。 但是您可以使它的使用更容易理解。


首先,您可以删除参数x ,该组合将返回部分应用程序。


 let add1Times2 = (>>) add1 times2 

其次,因为 >>是二进制运算符,您可以将其放在中间。


 let add1Times2 = add1 >> times2 

使用组合可使代码更清晰。


 let add1 x = x + 1 let times2 x = x * 2 //   let add1Times2 x = times2(add1 x) //   let add1Times2 = add1 >> times2 

在实践中使用合成运算符


合成运算符(像所有infix运算符一样)比常规函数具有较低的优先级。 这意味着在合成中使用的函数可以不带括号而带有参数。


例如,如果“ add”和“ times”函数具有参数,则可以在编写过程中传递它们。


 let add nx = x + n let times nx = x * n let add1Times2 = add 1 >> times 2 let add5Times3 = add 5 >> times 3 // add5Times3 1 

只要功能的相应输入和输出匹配,功能就可以使用任何值。 例如,考虑以下代码两次执行一个函数:


 let twice f = f >> f // ('a -> 'a) -> ('a -> 'a) 

请注意,编译器已推断“ f ”接受并返回相同类型的值。


现在考虑“ + ”功能。 如前所述,输入是int ,但输出实际上是(int->int) 。 因此,“ + ”可用于“ twice ”。 因此,您可以编写:


 let add1 = (+) 1 //  (int -> int) let add1Twice = twice add1 //    (int -> int) // add1Twice 9 

另一方面,您不能写:


 let addThenMultiply = (+) >> (*) 

因为输入“ *”必须是int ,而不是int->int函数(这是加法的输出)。


但是,如果您更正第一个函数,使其仅返回int ,那么一切都会起作用:


 let add1ThenMultiply = (+) 1 >> (*) // (+) 1   (int -> int)   'int' // add1ThenMultiply 2 7 

如有必要,还可以通过“ << ”以相反的顺序执行合成:


 let times2Add1 = add 1 << times 2 times2Add1 3 

反向组合主要用于使代码更像英语(“类似于英语”)。 例如:


 let myList = [] myList |> List.isEmpty |> not //   myList |> (not << List.isEmpty) //    

组成与 传送带


成分和传送带之间的细微差别可能会让您感到困惑,因为 它们看起来很相似。


首先,查看管道的定义:


 let (|>) xf = fx 

所有这些使您可以将函数的参数放在其前面,而不是之后。 仅此而已。 如果函数具有多个参数,则输入应为最后一个参数(在当前参数集中,而不是根本不输入)。 前面看到的一个示例:


 let doSomething xyz = x+y+z doSomething 1 2 3 //      3 |> doSomething 1 2 //      

组成不同,不能替代管道。 在下面的示例中,即使数字3也不是一个函数,所以“输出”不能传递给doSomething


 3 >> doSomething 1 2 //  // f >> g      g(f(x))     : doSomething 1 2 ( 3(x) ) //   3   ! // error FS0001: This expression was expected to have type 'a->'b // but here has type int 

编译器抱怨值“ 3”必须是一种函数'a->'b


将此与组合的定义进行比较,该定义包含3个参数,其中前两个应为函数。


 let (>>) fgx = g ( f(x) ) let add nx = x + n let times nx = x * n let add1Times2 = add 1 >> times 2 

尝试使用管道而不是合成将导致编译错误。 在下面的示例中,“ add 1 ”是(部分)函数int->int ,它不能用作“ times 2 ”的第二个参数。


 let add1Times2 = add 1 |> times 2 //  // x |> f      f(x)     : let add1Times2 = times 2 (add 1) // add1   'int' // error FS0001: Type mismatch. 'int -> int' does not match 'int' 

编译器抱怨“ times 2 ”必须接受参数int->int ,即 是一个函数(int->int)->'a


其他资源


F#的教程很多,包括那些具有C#或Java经验的人的材料。 当您深入了解F#时,以下链接可能会很有用:



还介绍了其他几种开始学习F#的方法


最后,F#社区非常适合初学者。 在Slack上,由F#Software Foundation支持的聊天非常活跃,您可以自由加入初学者室。 我们强烈建议您这样做!


不要忘记访问俄语社区F#的网站 ! 如果您对学习语言有任何疑问,我们将很乐意在聊天室中讨论这些问题:



关于翻译作者


@kleidemos翻译
在F#开发人员俄语社区的努力下进行了翻译和编辑更改。 我们也感谢@schvepsss@shwars为本文准备发表。

Source: https://habr.com/ru/post/zh-CN413195/


All Articles