如果我有一个名为Person的类型和函数列表,例如......
let checks = [checkAge; checkWeight; checkHeight]
...其中每个函数都是类型(Person - > bool),我想做相当于......
checkAge >> checkWeight >> checkHeight
...但我事先并不知道列表中有哪些功能,我该怎么做?
我尝试了以下内容......
checks |> List.reduce (>>)
...但这会产生以下错误......
错误FS0001:类型不匹配。期待一个 (人 - > bool) - > (人 - > bool) - >人 - >布尔
但是给出了
(Person - > bool) - > (bool - >'a) - >人 - > 'a
“人物”类型与“bool”类型
不匹配
我做错了什么?
答案 0 :(得分:4)
如果您有执行某些转换的函数,>>
运算符很有用。例如,如果您有一个函数列表Person -> Person
,则可以将一个人转变为另一个人。
在您的情况下,似乎您有函数Person -> bool
并且您想要构建一个组合函数,如果所有函数都返回true
,则返回true。
使用List.reduce
你可以写:
checks|> List.reduce (fun f g -> (fun p -> f p && g p))
也许更简单的选择就是编写一个能够吸引人并使用List.forall
的函数:
let checkAll checks person = checks |> List.forall (fun f -> f person)
答案 1 :(得分:4)
你可能会以某种方式偶然发现一个可怕的概念。功能性编程的伏地魔(不要说他的名字!)显然是它的。
没有进一步的ado让我们直接进入代码:
type Person =
{ Name : string
Age : int
Weight : int
Height : int }
type Result =
| Ok of Person
| Fail
let bind f m =
match m with
| Ok p -> f p
| _ -> Fail
let (>=>) f1 f2 = f1 >> (bind f2)
let checkAge p =
if p.Age > 18 then Ok(p)
else Fail
let checkWeight p =
if p.Weight < 80 then Ok(p)
else Fail
let checkHeight p =
if p.Height > 150 then Ok(p)
else Fail
let checks = [ checkAge; checkWeight; checkHeight ]
let allcheckfunc = checks |> List.reduce (>=>)
let combinedChecks =
checkAge
>=> checkWeight
>=> checkHeight
let p1 =
{ Name = "p1"
Age = 10
Weight = 20
Height = 110 }
let p2 =
{ Name = "p2"
Age = 19
Weight = 65
Height = 180 }
allcheckfunc p1
combinedChecks p1
allcheckfunc p2
combineChecks p2
此时我可以抛出很多古怪的术语(不是真的,我无法......),但让我们看看我做了什么。
我删除了 bool 的返回值,并使用 转到其他类型(结果)(标记该关键字!) 确定或失败。
然后做了一个帮助(绑定)来包装和解开结果类型的东西。
还有一个新的运算符(&gt; =&gt; )来组合reduce中的内容。
请注意,失败的第一个检查功能会快速切断整个链,并且或多或少立即(不调用其他功能)返回失败。此外,你不会知道它在这个链中的哪个位置失败或者在任何失败之前的哪些功能确实确定。
还有一些方法可以随着时间积累错误,以便获得类型的反馈:&#34; checkAge返回失败,但其他人是{{3} }
代码大部分都是从这里偷来的:great success"
你可能想要了解Wlaschin的整个网站,如果需要的话,还可以阅读更多更精细的细节。
祝你旅途愉快http://fsharpforfunandprofit.com/posts/recipe-part2/。 ; - )
脚注:通常称为Ivory Tower。它没有完全完成,上面的代码没有,但没关系......我认为它会适用于你的情况......
答案 2 :(得分:4)
看起来Railway oriented programming在这里很合适。 如果你选择这条路线,你基本上有两种选择。 你可以全押或快速路线。
快速路线
您重写验证函数以取Person option
而不是仅仅Person
。
let validAge (record:Record option) =
match record with
| Some rec when rec.Age < 65 && rec.Age > 18 -> record
| None -> None
现在你应该可以轻松链接你的功能了。
checks |> List.reduce (>>)
全部
或者,如果你很懒,并且不想在每个验证函数中匹配..你可以写一些代码。 (样本取自[1])
首先要做一些设置。 我们将定义一个特殊的返回类型,因此我们可以获得有意义的错误消息。
type Result<'TSuccess,'TFailure> =
| Success of 'TSuccess
| Failure of 'TFailure
绑定函数,将验证绑定在一起
let bind switchFunction =
function
| Success s -> switchFunction s
| Failure f -> Failure f
您还必须重写验证功能。
let validAge (record:Record) =
if record.Age < 65 && record.Age > 18 then Success input
else Failure "Not the right age bracket"
现在与
结合使用checks |> List.reduce (fun acc elem -> acc >> bind elem)
无论哪种方式,请查看原始文章。 您可以使用其中的更多内容:)
编辑:我刚刚注意到我再次写这个答案太慢了。 此外,我认为Helge也比我更好地解释了这个问题。