【问题标题】:F# Pipelines access data from pipeline stages aboveF# Pipelines 从上面的管道阶段访问数据
【发布时间】:2012-09-26 08:49:27
【问题描述】:

我写过这样的函数

let GetAllDirectAssignmentsforLists (spWeb : SPWeb) =    
  spWeb.Lists 
  |> Seq.cast<SPList> 
  |> Seq.filter(fun l -> l.HasUniqueRoleAssignments) 
  |> Seq.collect (fun l -> l.RoleAssignments 
                           |> Seq.cast<SPRoleAssignment> 
                           |> Seq.map(fun ra -> ra.Member)
                 )
  |> Seq.filter (fun p -> p.GetType().Name = "SPUser")
  |> Seq.map(fun m -> m.LoginName.ToLower())

我想返回一个元组,其中包含发送管道中的列表名称(取自l.Title)和m.LoginName.ToLower()

我是否可以从上述管道元素中获取一些东西?

一种方法当然是在管道的第二阶段对返回值进行元组处理,然后将标题一直向下传递....但这会污染代码,所有后续阶段都必须接受并返回元组values 只是为了在最后阶段获取值。

不知道有没有干净简单的方法....

另外,在管道(fun p -&gt; p.GetType().Name = "SPUser") 的第 4 阶段,我可以使用 if here 来比较类型吗?而不是将typename 转换为string 然后匹配字符串?

【问题讨论】:

    标签: f#


    【解决方案1】:

    我们利用Seq.filterSeq.map 可以在不改变结果的情况下推入Seq.collect 的事实。在这种情况下,l 仍然可以访问。

    最后一个filter 函数更适合与type test operator :? 一起使用。

    let GetAllDirectAssignmentsforLists(spWeb: SPWeb) =    
        spWeb.Lists 
        |> Seq.cast<SPList> 
        |> Seq.filter (fun l -> l.HasUniqueRoleAssignments) 
        |> Seq.collect (fun l -> l.RoleAssignments 
                                 |> Seq.cast<SPRoleAssignment> 
                                 |> Seq.map (fun ra -> ra.Member)
                                 |> Seq.filter (fun p -> match box p with
                                                         | :? SPUser -> true
                                                         | _ -> false)
                                 |> Seq.map (fun m -> l.Title, m.LoginName.ToLower()))
    

    为了进一步简化,您可以将Seq.mapSeq.filter 的系列更改为Seq.choose

    Seq.choose (fun ra -> match box ra.Member with
                          | :? SPUser -> Some (l.Title, ra.Member.LoginName.ToLower())
                          | _ -> None)
    

    【讨论】:

    • 什么是“火柴盒”?是 .NET 的装箱/拆箱吗?
    • 是的。如果您的类型是引用类型,则这是多余的。只需包含box,因为我不知道您的应用领域。
    【解决方案2】:

    虽然您可以通过提升 collect 中的其余计算来解决问题,但我认为您可以通过使用 序列表达式 而不是流水线来使代码更具可读性。

    我无法运行代码来测试它,但这应该是等效的:

    let GetAllDirectAssignmentsforLists (spWeb : SPWeb) = seq {
      // Corresponds to your 'filter' and 'collect'
      for l in Seq.cast<SPList> spWeb.Lists do
        if l.HasUniqueRoleAssignments then
          // Corresponds to nested 'map' and 'filter'
          for ra in Seq.cast<SPRoleAssignment> l.RoleAssignments do
            let m = ra.Member
            if m.GetType().Name = "SPUser" then 
              // This implements the last 'map' operation
              yield l.Title, m.LoginName.ToLower() }
    

    上面的代码更接近于@pad 的版本而不是你的原始代码,因为其余的计算嵌套在for 下(对应于collect 下的嵌套),所以你可以看到所有变量已经在范围内 - 例如您需要的 l

    序列表达式的好处是您可以使用 F# 结构,例如 if(而不是 filter)、for(而不是 collect)等。另外,我认为它更适合编写嵌套操作(您需要在此处将变量保持在范围内),因为它仍然具有很高的可读性并保持熟悉的代码结构。

    【讨论】:

    • 序列解析语法 seq { .. } 是完美的功能特性(就像 C# LINQ 一样,它受到 Haskell 列表解析的启发)。我认为流水线很适合与其他类型一起工作,但是序列表达式是专门为简化集合处理而设计的,那么为什么不使用它们呢?特别是如果它使您的代码更具可读性...
    猜你喜欢
    • 2021-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-25
    • 1970-01-01
    • 1970-01-01
    • 2020-03-05
    • 1970-01-01
    相关资源
    最近更新 更多