如何实现带条件的List Monad(计算表达式)?
How to implement the List Monad (computation expression) with a condition?
我正在尝试了解如何使用 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)
}
我的问题是,如何向这个计算表达式添加条件?具体来说,您如何将其更改为 return 仅在 x 值严格小于 y 值的情况下的元素列表? (我们以后不要过滤掉它)。
自 computation expressions can be parameterized 以来,您可能首先想到尝试这样的事情:
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)
元组的类型错误而失败:
This expression was expected to have type 'int
' but here has type ''a * 'b
'
还有两个编译器警告:在 FilteringListMonad 构造函数中 x < y
表达式的 y
处,有一个警告:
This construct causes code to be less generic than indicated by the type annotations. The type variable 'a
has been constrained to be type ''b
'.
并且在 let! x = [1 .. 10]
表达式中的数字 1
上,有一个警告:
This construct causes code to be less generic than indicated by the type annotations. The type variable 'b
has been constrained to be type 'int
'.
所以在这两个约束之间,return类型的计算表达式(一个'b list
)已经被约束为int list
,但是你的表达式是return取而代之的是 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,它们是值类型,因此不实现任何接口。这导致了错误
This expression was expected to have type 'System.IComparable
' but here has type '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! []
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 [])
)
我正在尝试了解如何使用 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)
}
我的问题是,如何向这个计算表达式添加条件?具体来说,您如何将其更改为 return 仅在 x 值严格小于 y 值的情况下的元素列表? (我们以后不要过滤掉它)。
自 computation expressions can be parameterized 以来,您可能首先想到尝试这样的事情:
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)
元组的类型错误而失败:
This expression was expected to have type '
int
' but here has type ''a * 'b
'
还有两个编译器警告:在 FilteringListMonad 构造函数中 x < y
表达式的 y
处,有一个警告:
This construct causes code to be less generic than indicated by the type annotations. The type variable
'a
has been constrained to be type ''b
'.
并且在 let! x = [1 .. 10]
表达式中的数字 1
上,有一个警告:
This construct causes code to be less generic than indicated by the type annotations. The type variable
'b
has been constrained to be type 'int
'.
所以在这两个约束之间,return类型的计算表达式(一个'b list
)已经被约束为int list
,但是你的表达式是return取而代之的是 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,它们是值类型,因此不实现任何接口。这导致了错误
This expression was expected to have type '
System.IComparable
' but here has type '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! []
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 [])
)