使用计算表达式

时间:2016-05-24 09:40:27

标签: f# functional-programming

我有一个生成一些元素并在List中返回它们的函数。该函数采用参数来过滤返回的元素。该函数如下所示:

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)。

4 个答案:

答案 0 :(得分:7)

因为要求作为答案:

最简单的方法是使用List.collect来完成地图,并在一个步骤中组合列表。

这种情况下的解决方案看起来像

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

答案 1 :(得分:5)

let result = 
    [
        yield! [ 100 .. 100 .. 1001]
        yield! [ 300 .. 300 .. 1001]
        yield! [ 500 .. 500 .. 1001]
        yield! [ 3 .. 3 .. 1001]
    ]

值得注意的是:使用Python列表后,我发现它们更友好,因为您可以使用值1000而不是值1001来表示F#。

如果您的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

答案 2 :(得分:2)

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

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

答案 3 :(得分:1)

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

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

然后使用List.ofSeq

计算列表