我仍然对如何阅读功能签名感到困惑。
Option.map签名如下:
/// map f inp evaluates to match inp with None -> None | Some x -> Some (f x).
/// mapping: A function to apply to the option value.
/// option: The input option.
val map : mapping:('T -> 'U) -> option:'T option -> 'U option
但是,我不知道签名意味着什么。
我将其读作以下内容:
有一个名为地图的功能,它将一个功能作为输入,我们称之为"映射" ,它将会产生一个结果也是我们称之为"选项" 的功能。
映射参数:
mapping:('T -> 'U)
我们作为输入传递的函数将 Titanium (即' T)作为输入并产生 Uranium (即' U)as输出
选项返回
option:'T option -> 'U option
我们会调用地图功能"选项" 的输出。 因此,执行地图功能返回的"选项" 也是一个函数,如上所述。它采用 Titanium选项并产生铀选项。
示例:
type String20 = String20 of string
type Name = { First:String20
Last:String20
Suffix:String20 option }
let tryCreateName (first:string) (last:string) (suffix:string option) =
let isValid = [first; last]
|> List.forall (fun x -> x.Length > 2 && x.Length <= 20)
if isValid then
Some { First = String20(first);
Last = String20(last);
Suffix = Option.map String20 suffix }
else None
以下表达式如何映射:
Option.map String20 suffix
基于上面的表达式,&#34;返回的函数&#34; Titanium选项 - &gt; 铀选项?
答案 0 :(得分:4)
首先看看Option.map<'T,'U> Function (F#)并注意
表达式映射f inp评估以匹配inp与None - &gt;没有| 一些x - &gt;一些(f x)。
因此,我们将此评论转换为工作代码。第一个map是Option类型的方法,但是为了使它更容易,我们将它变成一个类型之外的函数,并且为了避免与其他map函数冲突,我们将它命名为OptionMap。
let OptionMap = f inp =
match inp with
| None -> None
| Some x -> Some (f x)
现在获取此功能的签名只需将其发送给F#Interactive
val OptionMap : f:('a -> 'b) -> inp:'a option -> 'b option
为了使类型明显,我们将输入函数的参数。
let optionMap (f : ('a -> 'b)) (inp : 'a option) : 'b option =
match inp with
| None -> None
| Some x -> Some (f x)
现在测试一下,我们可以使用
let (test : String20 option) = optionMap String20 (Some("something"))
printfn "test: %A" test
// test: Some (String20 "something")
那么在OptionMap中发生了什么让它发挥作用
如果我们添加一些打印语句,请看看会发生什么
let optionMap (f : ('a -> 'b)) (inp : 'a option) : 'b option =
printfn "f: %A" f
printfn "inp: %A" inp
match inp with
| None -> None
| Some x ->
let result = Some (f x)
printfn "result: %A" result
result
我们得到了
f: <fun:test@63>
inp: Some "something"
result: Some (String20 "something")
我们看到f是一个函数,它是String20
那么String20如何成为一个函数呢?
如果我们将String20发送到F#Interactive,它会给出。
> String20;;
val it : arg0:string -> String20 = <fun:clo@4>
因此String20是一个接受字符串并返回String20类型的函数。那种气味的构造函数。
让我们在F#中进行测试。
> let test1 = String20 "something";;
val test1 : String20 = String20 "something"
果然,String20是一个构造函数,但我们没有像在面向对象的世界中那样专门创建一个构造函数。
你必须考虑一种类型,即使是一个有区别的联盟也有构造函数。构造函数不是专门编写的,但确实存在。所以String20是一个构造函数,它接受一个值,一个字符串,这是一个具有Option.map函数的正确类型签名的函数。
我给出了更多细节的答案,以便人们可以学习如何解决问题的过程,并将内部工作视为解决这些问题的工具。
要了解有关函数式编程如何工作的低级详细信息,请了解lambda calculus。我知道学习它的最好方法是阅读Greg Michaelson的An Introduction To Functional Programming Through Lambda Calculus并查看lambda calculus tag中的信息。
另外,如果你喜欢这本书,你应该买一本而不是免费版。
答案 1 :(得分:4)
有两种观察方式。
let f x y = x + y
// val f: x:int -> y:int -> int
一种方法是,函数f
采用x
类型int
和y
类型int
两个参数,并返回{{ 1}}。所以我可以提供两个参数并获得结果:
int
另一种方式是该函数接受一个类型为let a = f 4 5
// val a: int = 9
的{{1}}参数,并返回另一个函数,该函数接受x
类型int
的一个参数,并返回y
。所以我可以提供一个参数并得到一个函数作为结果:
int
数学上,它始终是第二种方式 - 所有函数都是单参数函数。这个概念对于使用高阶函数进行编程非常方便。 但第一种方式通常对人类来说更容易理解,因此您经常会看到所讨论的函数就像它们采用多个参数一样。
答案 2 :(得分:2)
通常,如果您有一个函数T1 -> T2 -> ...
并将其应用于一个参数,则会得到一个函数T2 -> ...
。在Option.map
的情况下,T1
本身就是一个函数,但这与参数的应用方式无关。
我发现调用函数&#34;选项&#34;,字符串&#34; Titanium&#34;还有一种名为String20&#34; Uranium&#34;的类型,所以我会坚持使用类型名称。
你问&#34;返回功能&#34;将字符串选项映射到String20选项的位置在表达式Option.map String20 suffix
中。它只是
Option.map String20
由于String20
从字符串构造String20,Option.map String20
是一个函数,如果存在则将字符串映射到String20,否则映射到None。
如果您编写Option.map String20 suffix
,此函数将应用于suffix
,这将是一个字符串选项。此表达式的结果是String20选项。
答案 3 :(得分:2)
String20
是String20
歧视联盟案例的案例构造函数。它是类型为string -> String20
的功能。因此,string
代替'T
,而String20
取代'U
mapping
,Option.map
。{/ 1} >