【问题标题】:Append multiple List with computation expression使用计算表达式附加多个列表
【发布时间】:2023-04-10 11:19:02
【问题描述】:

我有一个函数可以生成一些元素并在列表中返回它们。该函数接受一个参数来过滤返回的元素。函数如下所示:

let create x = [1..1000] |> List.filter (fun c -> (c % x) = 0)

现在我想用多个条件调用这个函数并将所有结果附加到一个列表中。

一开始我是这么想的:

let result = (create 100) 
            |> List.append (create 300) 
            |> List.append (create 500) 
            |> List.append (create 3)
printf "%A" result

但我没有发现这个解决方案优雅且重复使用 List.append 表达式。

我尝试了第二种解决方案:

let result = [100; 300; 500; 3] |> List.map (fun c -> create c) 
                                |> List.reduce (fun acc c -> acc |> List.append c)
printf "%A" result

这个解决方案更简洁,但我不喜欢它。我发现它不太清楚,也更难以猜测我们试图实现的目标。

所以,我一直在寻找第三种解决方案,并尝试用计算表达式实现一个新的解决方案:

type AppendBuilder() =
    member this.Bind (x, f) = f x |> List.append x
    member this.Return x = x

然后像这样使用它:

let append = new AppendBuilder()
let result = append {
    let! a = create 100
    let! b = create 300
    let! c = create 500
    let! d = create 3

    return []
}
printf "%A" result

最后一个解决方案有效,我更喜欢它。我发现它更清晰,更容易理解我们在做什么,并且没有重复的 List.append 表达式。但是还是有问题。我不喜欢return [] 部分,这似乎不自然且不清楚。但是这个技巧是让它发挥作用所必需的。

第三个解决方案是我最喜欢的,但我不确定这是解决我的问题的正确方法。它解决了问题,我得到了正确的结果,但我不确定这是否是正确的方法。

我能否保留第三种解决方案而不会混淆我的意图?解决方案是否足够清晰以保持原样?我正在寻找一种更专注于操作的解决方案(这里调用 create x),而不需要重复表达来寄生代码(这里调用 List.append)。

【问题讨论】:

  • [100;300;500;3] |> List.collect create怎么样
  • 我更喜欢 @JohnPalmer 的解决方案,而不是所有已发布的答案。它充分利用了库函数,并且 - 与所有发布的解决方案相比 - 允许参数 ([100;300;500;3]) 来自先前的计算步骤。
  • 是的,最好将您的计算表示为数据的转换作为起点,然后在其他要求(例如性能)需要时远离它。
  • 感兴趣的:Lambda Lifting 不是针对这个特定问题,而是针对一般重构功能代码的概念。

标签: f# functional-programming


【解决方案1】:

因为这是作为答案要求的:

最简单的方法是使用List.collect,它本质上是在一个步骤中完成映射并组合列表。

这种情况下的解决方案是这样的

[100;300;500;3] |> List.collect create

【讨论】:

    【解决方案2】:
    let result = 
        [
            yield! [ 100 .. 100 .. 1001]
            yield! [ 300 .. 300 .. 1001]
            yield! [ 500 .. 500 .. 1001]
            yield! [ 3 .. 3 .. 1001]
        ]
    

    注意:使用 Python 列表后,我确实发现它们更友好,因为您可以在 F# 中使用值 1000 而不是值 1001

    如果您的 create 函数始终是 step function,那么使用 List 的内置功能更有意义。如果您打算使用无法利用 List 内置功能的create 函数,那么John Palmer 的注释更有意义。

    let result = 
        [100;300;500;3] 
        |> List.collect create
    

    Anton Schwaighofer 在评论中建议的一个变体是允许 create 函数的值作为参数传入。

    let makeList parms =
        parms
        |> List.collect create
    

    然后被用作

    let result = makeList [100;300;500;3]
    

    甚至pointfree

    let makeList = List.collect create
    

    【讨论】:

      【解决方案3】:

      如果第三个是你最喜欢的版本,那么你可能想要使用列表推导:

      let result = 
          [ yield! create 100
            yield! create 300
            yield! create 500
            yield! create 3 ]
      

      【讨论】:

        【解决方案4】:

        您可以从 seq 计算的惰性中受益:

        seq {
            yield! create 100
            yield! create 300
            yield! create 500
            yield! create 3
        } |> List.ofSeq
        

        然后使用List.ofSeq计算列表

        【讨论】:

          猜你喜欢
          • 2021-06-02
          • 1970-01-01
          • 1970-01-01
          • 2012-09-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多