如何使用条件实现列表单元(计算表达式)



我试图了解如何使用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#

中的计算表达式

一个人可以使用有用的ReturnFromZero延长计算表达式:

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 [])
      )

相关内容

  • 没有找到相关文章