【问题标题】:All picks of a list in F# - more elegant and simpleF# 中列表的所有选择 - 更加优雅和简单
【发布时间】:2010-11-14 10:39:39
【问题描述】:

有人可以提出更好和/或更优雅的实现吗:

让每个 xs = 让 rec each' acc 左右 = 与 | [] -> ACC |右 -> 让 new_left = 左 @ [List.hd 右] 让下一个 = List.tl 对 let result = (List.hd right), left @ next each' (result::acc) new_left next 每个' [] [] xs

这样做:

> 每个 [1..3];; 验证它: (int * int list) list = [(3, [1; 2]); (2, [1; 3]); (1, [2; 3])]

这个函数也可以反向返回结果。这个想法是将所有元素作为包含一个元素和其余元素列表的元组。

【问题讨论】:

    标签: list f# functional-programming simplify


    【解决方案1】:

    我使用折叠的其他提议。它是线性函数 O(N)。但绝对不像 DannyAsher 的解决方案那么优雅和简单:

    let each5 xs =  let fu (left, next, acc) x = left@[x], List.tl next, (x, left@(List.tl next))::acc
                    let (x, y, res) = List.fold fu ([], xs, []) xs
                    res
    

    【讨论】:

    • 5000 个元素的速度比 kvb 快大约 3 倍。
    【解决方案2】:

    这里的语义略有不同,但从你给出的例子来看Set 可能很合适:

    let each xs =
        let X = set xs                           
        [ for x in xs -> (x, X - set [x]) ]
    
    
    > fsi.AddPrinter( fun (x:Set<int>) -> sprintf "%A" (Set.to_list x))
    > each [1..3];;
    > val it : (int * Set<int>) list = [(1, [2; 3]); (2, [1; 3]); (3, [1; 2])]
    
    // Edited as per comments.
    

    【讨论】:

    • 非常优雅的解决方案!但是你认为它的算法复杂度是多少?
    • 我相信这应该是 O(n log n)。
    • @DannyAsher - 我很确定它充其量是 O(n^2),因为您每次通过循环都在计算 set xs。提升该计算将使函数显着更快。
    • 嗨 kvb - 对不起,我假设 set xs 将被排除在循环之外。第一个编辑实际上有这个,但我省略了它。有时简洁会比我更好.
    • 我认为两个编辑是一样的。并且编译器“看到” xs 是相同的集合,并且不会每次都重新计算它(记住懒惰)。所以我认为无论如何都需要O(n log n)。可能只有探查器或调试器可以证明谁是对的......
    【解决方案3】:

    怎么样:

    let rec each = function
    | x :: xs -> (x,xs) :: (each xs |> List.map (fun (y,ys) -> (y,x::ys)))
    | [] -> []
    

    或尾递归等价物(以相反的顺序生成列表):

    let each lst =
      let rec helper acc seen = function
      | [] -> acc
      | x :: xs -> 
          helper ((x,seen)::(acc |> List.map (fun (y,ys) -> (y,x::ys)))) (x::seen) xs
      helper [] [] lst
    

    【讨论】:

    • 抱歉,我用更合理的值 4000 重新运行了它,它运行良好,+1。
    • 顺便说一句,“as”是 VS 2010 Beta 中的保留字。
    • @gradbot - 谢谢,我更改了 a,as,b,bs -> x,xs,y,ys 以避免关键字问题。这就是在没有编译器的情况下发布的危险......
    • 你的第二个函数比第一个函数快两倍以上。
    【解决方案4】:
    let each l = l |> List.map (fun x -> x, List.filter (fun y -> y <> x) l)
    

    注意:这个函数是 O(n^2)。考虑改用 Seq.map 和 Seq.filter:

    let each l = l |> Seq.map (fun x -> x, Seq.filter (fun y -> y <> x) l)
    

    Seq 版本的性能为 O(n)。

    【讨论】:

    • 我猜这取决于你如何定义 Big O Seq 是 O(n) 但是如果你迭代它那么它将是 O(N^2) 因为函数返回一个集合 n 与大小的元素n.
    • 谢谢你我不知道@的行为 - 因为你的序列一相当优越我刚刚删除了我的
    • 顺便说一句,你的函数确实有一个缺陷:尝试每个 (1::2::2::3::[])
    • 让每个 l = l |> Seq.map (fun x -> x, Seq.filter (() x) l)
    • 第二个函数的时间比第一个函数快 1400 倍。
    【解决方案5】:

    这并不比原来的解决方案好多少(如果有的话),但就是这样。此版本通过使用实用函数将反转的左侧列表与右侧的尾部合并来避免列表追加。此外,它使用模式匹配而不是 head 和 tail 函数。

    let rec ljoin revleft right =  
      match revleft with 
           | [] -> right 
           | (x::xs) -> ljoin xs (x::right)                                                                                   
    let each xs =
        let rec each' acc left right =
           match right with
           | [] -> acc
           | (y::ys) ->
               let result = y, ljoin left ys 
               each' (result::acc) (y::left) ys
        each' [] [] xs
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-22
      • 2013-06-21
      • 1970-01-01
      • 1970-01-01
      • 2013-04-25
      • 1970-01-01
      • 2018-10-23
      • 2018-08-27
      相关资源
      最近更新 更多