【问题标题】:F# nested List.iter callsF# 嵌套 List.iter 调用
【发布时间】:2016-02-24 03:12:01
【问题描述】:

我有一个 F# 函数,我想尝试改变一些参数并测试所有这些组合。这是正确的方法吗? (括号有点密集...):

let MyFunc a b c x y z = 
  ...
  q


let UploadResult a b c x y z q =
  ...
  ()


let a = 5.0
let b = 0
let c = System.DateTime.Today
let xList = [-1.0; 0.0; 1.0]
let yList = [2; 4; 6; 8; 10]
let zList = [0.1; 0.001]

xList  |> List.iter (fun x ->
(yList |> List.iter (fun y ->
(zList |> List.iter (fun z ->
MyFunc a b c x y z 
|> UploadResult a b c x y z ))) ))
|> ignore

所以我想上传 3x5x2=30 个结果,并且写的很好。感谢您的任何建议。

【问题讨论】:

  • 问题是什么,或者您只想进行代码审查? (在这种情况下,您可能会先清理一下 - 例如 Let a = 5.0 将不起作用(语法错误)并且您的缩进很远
  • 好吧,在尝试运行我的草稿之前,我在发帖时有点超前,因为应用程序非常大(至少 IDE 没有抱怨)。但我希望笼统地问一下嵌套 List.iter 是否是惯用的方法(就像我会反身地用另一种语言编写嵌套的 For 循环一样)。
  • 嵌套的 for 循环在这里也可以工作;可以做的是在循环之前在两个函数中部分应用 a、b 和 c
  • 谢谢。以上内容可能太明显了,无法问。但很多时候,对于 F# 的新手,看到新的聪明方法时,我会感到惊讶和印象深刻。

标签: loops f# iterator nested


【解决方案1】:

对于像上传结果这样的纯命令式操作,我真的不会太担心。在您的代码示例中使用List.iter 可以解决问题。我可能更喜欢for 循环,因为很明显这段代码具有重要的副作用:

for x in xList do
  for y in yList do
    for z in zList do
      MyFunc a b c x y z |> UploadResult a b c x y z

如果你想做一些聪明的事情,你可以编写一个函数,从两个列表中生成所有参数组合:

let product xl yl = 
  seq { for x in xl do
          for y in yl do
            yield x, y }

好消息是您还可以多次使用它:product xList (product yList zList)。这将为您返回一个可以再次迭代的元组列表:

for (x,y), z in product (product xList yList) zList do
    MyFunc a b c x y z 
    |> UploadResult a b c x y z

这样不太好的事情是你最终会得到嵌套的元组——这就是为什么我可能只使用一个普通的循环。 (或者如果你总是有 3 个列表,那么另一个答案中的解决方案类似于 product,但针对 3 个列表进行了优化)

【讨论】:

  • 感谢 Tomas 的另一个有见地的回复。我需要仔细研究您的“产品”和 Functional_S 的“烫发”中略有不同的语法的细节。
【解决方案2】:

事实上,您的主要目标是创建一个由多个列表组成的跨积(或笛卡尔积),并且在 F# 中有几个被认为是“良好实践”的选项开发者:

1。 (删除for-comprehension,因为其他答案已经建议了这个)

2。使用计算表达式(在其他函数式编程世界中,它通常被称为 Monad):

type Product () =
    member this.Bind (l,f) = List.collect f l    
    member this.Return n = [n]

let ret02 = Product() {
    let! x = xList
    let! y = yList
    let! z = zList
    MyFunc a b c x y z
    |> UploadResult a b c x y z
}

3。如果您只担心括号,请使用高优先级、右关联反向管道,(more info)

let inline (^<|) f a = f a

然后,您的代码将需要进行最少的修改(尽管仍然不是很干净):

let ret03 =
    xList  |> List.iter ^<| fun x ->
    yList  |> List.iter ^<| fun y ->
    zList  |> List.iter ^<| fun z ->
        MyFunc a b c x y z
        |> UploadResult a b c x y z
        |> ignore

【讨论】:

  • 非常感谢 bytebuster。我很高兴你提到了计算表达式。我一直在研究 F# for Fun and Profit 的教程(以及一些 Tomas 和 Syme 的论文)。我根本没有看到它,但希望这可能与这里有关。所以我真的很高兴你的#2,我也喜欢#3 收拾东西的方式。
  • 也感谢#3 中的链接。那里的比较有助于理解运算符如何避免括号。非常好。
  • @RomnieEE 我在我的项目中广泛使用它。它让我在许多情况下避免使用括号。检查this answer 以获得一些几乎真实的代码示例。
【解决方案3】:
let MyFunc a b c x y z = 
    42

let UploadResult a b c x y z q =
    printfn "%A %A %A %A %A %A %A" a b c x y z q

let a = 5.0
let b = 0
let c = System.DateTime.Today
let xList = [-1.0; 0.0; 1.0]
let yList = [2; 4; 6; 8; 10]
let zList = [0.1; 0.001]

let perm xs ys zs =
    [for x in xs do
        for y in ys do 
            for z in zs do
                yield x,y,z]
let f (x,y,z) = MyFunc a b c  x y z |> UploadResult a b c  x y z
perm xList yList zList |> List.iter f

【讨论】:

  • 感谢 F_S 的回复。我很高兴已经获得了 3 个有用的答案,而且很难挑出一个。我自动假设使用 For 循环意味着按项目迭代,我知道这是单调的。你的用法很有启发性。再次感谢。
猜你喜欢
  • 2021-11-10
  • 2021-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-04
  • 2017-10-10
  • 2023-04-01
相关资源
最近更新 更多