功能思维。 第8部分

哈Ha! 新年假期结束后,我们将继续提供有关函数式编程的系列文章。 今天,我们将讨论通过签名理解函数以及定义函数签名的类型。 细节剪下!




不明显,但是F#具有两种语法:用于正则(有意义)表达式和用于类型定义。 例如:


[1;2;3] //   int list //   Some 1 //   int option //   (1,"a") //   int * string //   

类型的表达式具有普通表达式不同的特殊语法。 在使用FSI(FSharp Interactive)时,您可能已经注意到此语法的许多示例,例如 将显示每个表达式的类型以及其执行结果。


如您所知,F#使用类型推断算法,因此通常不需要在代码中显式地编写类型,尤其是在函数中。 但是要有效使用F#,您需要了解类型的语法,以便可以定义自己的类型,调试类型转换错误和读取函数签名。 在本文中,我将重点介绍函数签名中类型的使用。


以下是类型语法签名的一些示例:


 //   //   let add1 x = x + 1 // int -> int let add xy = x + y // int -> int -> int let print x = printf "%A" x // 'a -> unit System.Console.ReadLine // unit -> string List.sum // 'a list -> 'a List.filter // ('a -> bool) -> 'a list -> 'a list List.map // ('a -> 'b) -> 'a list -> 'b list 

通过签名了解功能


通常,即使仅研究函数的签名,您也可以对它的功能有所了解。 考虑一些示例,然后依次分析它们。


 int -> int -> int 

此函数接受两个int参数,并返回另一个int 。 最有可能的是,它是一种数学函数,例如加,减,乘或乘。


 int -> unit 

此函数采用int并返回unit ,这意味着该函数做了一些重要的副作用。 因为 它不会返回有用的值,副作用很可能会在IO中产生写入操作,例如日志记录,写入数据库或类似操作。


 unit -> string 

此函数不接受任何内容,但返回一个string ,这可能意味着该函数从空中接收了一个字符串。 由于没有显式输入,因此该函数可能在读取(例如从文件)或生成(例如随机字符串)时执行某些操作。


 int -> (unit -> string) 

该函数接受一个int并返回另一个函数,该函数将在调用时返回一个字符串。 同样,该功能可能会执行读取或生成操作。 输入可能会以某种方式初始化返回函数。 例如,输入可以是文件的标识符,并且返回的函数类似于readline() 。 或者,输入可能是随机字符串生成器的起始值。 我们不能肯定地说,但可以得出一些结论。


 'a list -> 'a 

该函数接受任何类型的列表,但仅返回此类型的一个值。 这可能表明该函数汇总了列表或选择了其元素之一。 类似的签名是List.sumList.maxList.head等。


 ('a -> bool) -> 'a list -> 'a list 

该函数有两个参数:第一个是将事物转换为bool (谓词)的函数,第二个是列表。 返回值是相同类型的列表。 谓词用于确定对象是否满足特定条件,以及该函数是否似乎根据谓词从列表中选择元素-true或false。 之后,它将返回原始列表的子集。 具有此签名的函数的一个示例是List.filter


 ('a -> 'b) -> 'a list -> 'b list 

该函数有两个参数:从类型'a到类型'b转换以及类型'a的列表。 返回值是'b类型的列表。 可以合理地假设该函数从列表'a获取每个元素,然后使用作为第一个参数传递的函数将其转换为'b ,然后返回列表'b 。 实际上, List.map是具有这种签名的函数的原型。


搜索带签名的库方法


函数签名对于查找库函数非常重要。 F#库包含数百个函数,一开始可能会造成混淆。 与面向对象的语言不同,您不能简单地通过点“输入对象”来查找所有相关方法。 但是,如果您知道所需功能的签名,则可以快速缩小搜索范围。


例如,您有两个列表,并且想要找到一个将它们组合为一个的函数。 所需功能将具有什么签名? 它必须将两个列表作为参数并返回第三个相同类型的列表:


 'a list -> 'a list -> 'a list 

现在,转到MSDN文档站点的“列表”模块 ,并查找类似的功能。 事实证明,只有一个具有这种签名的功能:


 append : 'T list -> 'T list -> 'T list 

你需要什么!


定义函数签名的本机类型


有一天,您将需要为所需的功能定义自己的类型。 可以使用关键字“ type”完成此操作:


 type Adder = int -> int type AdderGenerator = int -> Adder 

将来,您可以使用这些类型来限制函数参数的值。


例如,由于强制转换错误的限制,第二个声明将失效。 如果我们删除它(如第三个公告中所述),该错误将消失。


 let a:AdderGenerator = fun x -> (fun y -> x + y) let b:AdderGenerator = fun (x:float) -> (fun y -> x + y) let c = fun (x:float) -> (fun y -> x + y) 

测试功能标志理解


您对功能签名了解得很好吗? 检查您自己是否可以使用下面的签名创建简单的函数。 避免明确指定类型!


 val testA = int -> int val testB = int -> int -> int val testC = int -> (int -> int) val testD = (int -> int) -> int val testE = int -> int -> int -> int val testF = (int -> int) -> (int -> int) val testG = int -> (int -> int) -> int val testH = (int -> int -> int) -> int 

其他资源


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



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


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


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



关于翻译作者


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

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


All Articles