功能思维。 第二部分

朋友,我们继续了解函数式编程。 在本系列文章的第二部分中,您将熟悉这种开发范例的基本原理,并了解这种方法与面向对象或命令式编程的区别。




价值观与职能


再一次考虑这个简单的功能。


let add1 x = x + 1 

x在这里是什么意思:


  1. 从域(范围)中获取一些价值。
  2. 使用名称“ x ”提供此值,以便以后可以访问。

使用名称表示值称为绑定。 名称“ x ”被“绑定”到输入值。


因此,如果您计算一个输入值为5的函数,则会发生以下情况:无论原始定义中的“ x ”位于何处,都将设置值5,类似于文本编辑器中的“查找并替换”函数。


 let add1 x = x + 1 add1 5 //  «x» with «5» // add1 5 = 5 + 1 = 6 //  6 

重要的是要了解这不是一项任务。 “ x ”不是“插槽”,也不是具有可稍后更改的分配值的变量。 这是名称“ x ”与某个值的一次性关联。 该值是预定义的整数之一;无法更改。 即 一旦绑定x 不能更改 。 一旦与该值相关联的标签将永远与该值相关联。


该原则是功能思维的关键部分: 没有“变量”,只有值


作为价值的功能


如果再考虑一下,您会发现名称“ add1 ”本身只是对“将输入增加一的函数”的绑定。 函数本身独立于附加的名称。


通过引入let add1 x = x + 1 ,我们对F#编译器说:“每次看到名称“ add1 ”,将其替换为向输入加1的函数。 “ add1 ”称为函数值


要查看该函数不依赖于其名称,只需执行以下代码即可:


 let add1 x = x + 1 let plus1 = add1 add1 5 plus1 5 

如您所见,“ add ”和“ plus ”是绑定到同一功能的两个名称。


您始终可以通过其签名来识别值函数,该签名的标准格式为domain -> range 。 值函数的通用签名:


 val functionName : domain -> range 

简单的价值观


想象一下什么都不做并且总是返回5的操作。



这将是“恒定”操作。


如何用F#描述呢? 我们要告诉编译器:“每次看到名称c ,将其替换为5”。 像这样:


 let c = 5 

计算时将返回:


 val c : int = 5 

这次没有匹配的箭头,只有一个int。 从新的符号开始-等于其后得出的实际值的等号。 F#编译器知道此绑定具有一个已知值,该值将始终返回,即数字5。


换句话说,我们只是定义了一个常数,或者就F#而言,是一个简单的值。
您始终可以将简单值与值函数区分开,因为所有简单值都具有相似的签名:


 val aName: type = constant //  -   

简单值与 函数值| 简单含义与 价值函数


重要的是要理解,在F#中,与其他语言(例如C#)不同,简单值和值函数之间的差异很小。 这两种类型都是可以与名称相关联的值(使用let关键字),然后可以将它们传递到任何地方。 实际上,我们很快就会看到, 功能是可以作为输入传递给其他功能的值的想法是功能思维的关键方面之一。


请注意,简单值和值函数之间略有不同。 函数始终具有域和范围,并且必须“应用于”自变量以返回结果。 绑定后无需计算简单值。 使用上面的示例,如果我们想定义一个返回5的“常量函数”,则可以使用:


 let c = fun()->5 // or let c() = 5 

这些函数的签名如下所示:


 val c : unit -> int 

不是这样的:


 val c : int = 5 

稍后将提供有关unit ,函数语法和匿名函数的更多信息。


“价值观”与 “对象”


在函数式编程语言(例如F#)中,大多数情况称为“值”。 在诸如C#之类的面向对象的语言中,大多数东西称为“对象”。 “含义”和“对象”之间有什么区别?


如上所见,该值是域的成员。 整数域,字符串域,将整数映射到字符串的函数域,等等。 原则上,这些值是不可变的(不可更改)。 含义没有附加的行为。


规范定义中的对象是具有相关行为(方法)的数据结构的封装。 通常,对象必须具有状态(即可变的),并且更改内部状态的所有操作都必须由对象本身提供(通过“点”表示法)。


在F#中,即使原始值也具有一定量的“对象”行为。 例如,您可以通过一个点获取字符串的长度:


 «abc».Length 

但通常,对于F#中的标准值,我们将避免使用术语“对象”,将其保存为引用完整的类或提供方法的其他值。


命名值


用于值和函数名称的标准命名约定基本上是字母数字+下划线。 但是还有一些补充:


  1. 您可以在名称的任何部分添加撇号,但第一个字符除外。

 A'b'c begin' //   

  1. 后一种情况通常用作值的“不同”版本的标签:

 let f = x let f' = derivative f let f'' = derivative f' 

或与现有关键字同名的变量


 let if' btf = if b then t else f 

您还可以对任何字符串使用双反引号,以使其成为有效的标识符。


 ``this is a name`` ``123`` //  

您可能需要使用双重反引号技巧的情况:


  • 需要使用与关键字匹配的标识符时。

 let ``begin`` = «begin» 

  • 当您需要将自然语言用于业务规则,单元测试或BBD风格的可执行文档(例如Cucumber)时。

 let ``is first time customer?`` = true let ``add gift to order`` = () if ``is first time customer?`` then ``add gift to order`` // - let [<Test>] ``When input is 2 then expect square is 4``= // code here // BDD clause let [<Given>] ``I have (.*) N products in my cart`` (n:int) = // code here 

与C#不同,F#命名约定要求函数和值以小写字母开头,而不是以大写字母开头( camelCase而不是PascalCase ),除非它们旨在与其他 .NET语言进行交互 。 但是,类型和模块使用大写字母(开头)。


其他资源


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



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


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


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



关于翻译作者


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

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


All Articles