F# 如何编写函数列表?

F# 如何编写函数列表?,f#,F#,如果我有一个名为Person的类型和函数列表,例如 let checks = [checkAge; checkWeight; checkHeight] …其中每个函数都是(Person->bool)类型,我要做的是等效于 checkAge >> checkWeight >> checkHeight …但我事先不知道列表中有哪些函数,我该如何做 我尝试了以下方法 checks |> List.reduce (>>) …但这会产生以下错误 错误FS00

如果我有一个名为Person的类型和函数列表,例如

let checks = [checkAge; checkWeight; checkHeight]
…其中每个函数都是(Person->bool)类型,我要做的是等效于

checkAge >> checkWeight >> checkHeight
…但我事先不知道列表中有哪些函数,我该如何做

我尝试了以下方法

checks |> List.reduce (>>)
…但这会产生以下错误

错误FS0001:类型不匹配。期待 (人物->布尔)->(人物->布尔)->人物->布尔

但是给一个

(人物->布尔)->(布尔->'a)->人物->'a

类型“Person”与类型“bool”不匹配


我做错了什么?

如果您有执行某些转换的函数,则
>
运算符很有用。例如,如果您有一个函数列表
Person->Person
,可以将一个人变成另一个人

在您的例子中,似乎有函数
Person->bool
,您希望构建一个组合函数,如果所有函数都返回
true
,该函数将返回true

使用
List.reduce
可以编写:

checks|> List.reduce (fun f g -> (fun p -> f p && g p))
也许更简单的选择是编写一个函数,该函数接受一个人并使用
列表

let checkAll checks person = checks |> List.forall (fun f -> f person)

你可能不知何故偶然发现了一个可怕的概念。显然是伏地魔(别说他的名字!)的函数式编程

无需进一步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
type Person=
{Name:string
年龄:整数
重量:整数
高度:int}
类型结果=
|人的Ok
|失败
设bind f m=
匹配
|Ok p->f p
|->失败
让(>=>)f1 f2=f1>>(绑定f2)
让检查p=
如果p.年龄>18,则Ok(p)
否则失败
设校核重量p=
如果p.重量<80,则正常(p)
否则失败
设检查高度p=
如果p.高度>150,则正常(p)
否则失败
让检查=[检查年龄;检查体重;检查身高]
让allcheckfunc=checks |>List.reduce(>=>)
让我们来核对一下=
支票
>=>检查重量
>=>检查高度
设p1=
{Name=“p1”
年龄=10
重量=20
高度=110}
设p2=
{Name=“p2”
年龄=19
重量=65
高度=180}
allcheckfunc p1
组合支票p1
allcheckfunc p2
combineChecks p2
在这一点上,我可以抛出很多奇怪的行话(不是真的,我不能…),但让我们看看我做了什么

  • 我放弃了你的返回值bool,选择了另一种类型(Result),使用(标记该关键字!)OkFail

  • 然后让一个助手(绑定)从该结果类型中包装和展开内容

  • 和一个新的操作符(=>)来组合reduce中的内容

请注意,第一个检查函数Fail将缩短整个链,并且或多或少会立即(不调用其他函数)返回Fail。此外,您将不知道它在该链中的何处发生了故障,或者在任何故障之前的哪些功能实际上发生了正常

也有一些方法可以在进行过程中累积错误,这样您就可以得到一个类型为“检查返回失败的反馈,但其他的都是错误”

代码大部分是从这里偷来的:

你可能想阅读整个网站的Wlaschin,甚至更多,以进入更精细和更难的细节,如果需要的话

祝您在通往酒店高层的旅途中好运。;-)

脚注:这通常称为a。它还没有完全完成,上面的代码中没有,但没关系……我认为它在您的情况下会起作用……

看起来很适合这里。 如果你选择走这条路,你基本上有两个选择。 你可以全速前进,也可以走捷径

快速路线

您可以重写验证函数,以采用
Person选项
,而不仅仅是普通的
Person

let validAge (record:Record option) = 
        match record with
        | Some rec when rec.Age < 65 && rec.Age > 18 -> record
        | None -> None
全方位

或者,如果您懒惰并且不想在每个验证函数中匹配..,则可以编写更多代码。 (样本取自[])

首先要做一些设置。 我们将定义一个特殊的返回类型,以便获得有意义的错误消息

type Result<'TSuccess,'TFailure> = 
    | Success of 'TSuccess
    | Failure of 'TFailure
您还必须重写验证函数

let validAge (record:Record) = 
    if record.Age < 65 && record.Age > 18 then Success input
    else Failure "Not the right age bracket"
无论哪种方式,请查看原始文章。 您还可以使用更多内容:)

编辑:我只是注意到我再次写这个答案太慢了。
此外,我认为Helge对concetp的解释也比我好。

要使用
>
操作符,右边的函数必须以左边的结果类型作为参数。
checkAge
返回
bool
,但是
checkWeight
期望
Person
。您希望最终结果是什么nify?@TeaDrivenDev我正在尝试组合函数,这样我就可以一次性调用结果,而不是一次一个地遍历每个函数。这一切都是从我试图在这个F#Snippets页面改进代码开始的……谢谢你的回答。我实际上是在编写一个包含函数列表的函数来概括代码段ons,组合它们并运行组合结果。该代码段手动组合函数,因此它特定于所讨论的问题。我可以这样做吗?也许……但我也这样做了。;-@Brunner我刚刚回来更新我的问题,并添加我的答案,这与您和Helge的答案基本相同。是的,在查看该代码段时,它看起来是d对我来说就像ROP一样,我试图应用这个原则。我最终得到的代码与你的代码略有不同(我没有使用DU),但我认为原则是一样的。现在我必须决定h=谁的答案被接受!他们都非常感谢
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)