【发布时间】: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 不是针对这个特定问题,而是针对一般重构功能代码的概念。