我试图了解如何使用f#计算表达式,这肯定会困扰我。
以下示例对我来说很有意义。
type ListMonad() =
member o.Bind( (m:'a list), (f: 'a -> 'b list) ) = List.collect f m
member o.Return(x) = [x]
let list = ListMonad()
let test =
list {
let! x = [ 1 .. 10]
let! y = [2 .. 2 .. 20]
return (x,y)
}
我的问题是,如何在此计算表达式中添加条件?具体来说,您如何将其更改以返回仅在x值严格小于y值的元素列表?(以后不要过滤出来(。
由于可以对计算表达式进行参数化,因此您可能首先想尝试这样的尝试:
let filterAndCollect (pred : 'a -> 'b -> bool) (f : 'a -> 'b list) (m : 'a list) =
let f' a = [ for b in f a do if pred a b then yield b ]
List.collect f' m
type FilteringListMonad(pred) =
member o.Bind( (m:'a list), (f: 'a -> 'b list) ) = filterAndCollect pred f m
member o.Return(x) = [x]
let filteredList = FilteringListMonad(fun x y -> x < y)
let test2 =
filteredList {
let! x = [1 .. 10]
let! y = [2 .. 2 .. 20]
return (x,y)
}
但是,在(x,y)
元组上,这会失败:
此表达式有望具有'
类型int
'的类型,但此处具有''a * 'b
'
也有两个编译器警告:在FilteringListMonad构造函数中的x < y
表达式的y
上,有一个警告:
此构造导致代码不如类型注释所指示的通用性。类型变量
'a
被限制为类型为''b
'。
以及在let! x = [1 .. 10]
表达式中的1
上,有一个警告:
此构造导致代码不如类型注释所指示的通用性。类型变量
'b
被限制为类型为'int
'。
因此,在这两个约束之间,计算表达式的返回类型(A 'b list
(被限制为int list
,但是您的表达式是返回int * int list
。在考虑了类型约束之后,您可能会得出结论认为这无法正常工作。但是有一种方法可以使其起作用。关键是要意识到,在此示例中,将是计算表达式输出的'b
类型实际上是 tuple int * int
,因此您将谓词函数重写为实际上仅采用'b
类型,然后一切都起作用:
let filterAndCollect (pred : 'b -> bool) (f : 'a -> 'b list) (m : 'a list) =
let f' a = [ for b in f a do if pred b then yield b ]
List.collect f' m
type FilteringListMonad(pred) =
member o.Bind( (m:'a list), (f: 'a -> 'b list) ) = filterAndCollect pred f m
member o.Return(x) = [x]
let filteredList = FilteringListMonad(fun (x:int,y:int) -> x < y)
let test2 =
filteredList {
let! x = [ 1 .. 10]
let! y = [2 .. 2 .. 20]
return (x,y)
}
请注意,我还必须指定谓词函数的输入的类型。没有这些,f#将它们推广为"实现System.IComparable
的任何类型,但是我通过int
s传递,这些类型是值类型,因此没有实现任何接口。这导致了错误
此表达式预计具有'
System.IComparable
'的类型,但此处具有类型的'int
'。
但是,将两个参数声明为谓词,就像 int
做到了。
op已经接受了答案,我将提供另一种方法,这可能有助于帮助了解f#
中的计算表达式一个人可以使用有用的ReturnFrom
和Zero
延长计算表达式:
type ListMonad() =
member o.Bind (m, f) = List.collect f m
member o.Return x = [x]
member o.ReturnFrom l = l : _ list
member o.Zero () = []
let listM = ListMonad()
ReturnFrom
允许我们使用return! []
返回空列表,从而启用过滤。Zero
是一个手牌,因为如果使用else
分支,则使用Zero
。
这使我们能够像这样过滤:
let test =
listM {
let! x = [ 1 .. 10]
let! y = [2 .. 2 .. 20]
if x % y = 0 then
return (x,y)
// By defining .Zero F# implicitly adds else branch if not defined
// else
// return! []
}
f#将将计算扩展到大致类似的东西:
let testEq =
[ 1 .. 10]
|> List.collect
(fun x ->
[2 .. 2 .. 20]
|> List.collect (fun y -> if x % y = 0 then [x,y] else [])
)