views:

298

answers:

5

Could someone propose better and/or more elegant implementation of this:

let each xs = 
    let rec each' acc left right = 
        match right with
        | [] -> acc
        | right ->  let new_left  = left @ [List.hd right]
                    let next   = List.tl right
                    let result = (List.hd right), left @ next
                    each' (result::acc) new_left next
    each' [] [] xs

It do that:

> each [1..3];;
val it : (int * int list) list = [(3, [1; 2]); (2, [1; 3]); (1, [2; 3])]

This function could return the result in reverse direction, too. The idea is to get all elements as tuples with an element and list of rest elements.

+2  A: 
let each l = l |> List.map (fun x -> x, List.filter (fun y -> y <> x) l)

Note: this function is O(n^2). Consider using Seq.map and Seq.filter instead:

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

Seq version has a performance of O(n).

Juliet
I guess depending on how you define Big O Seq is O(n) however if you iterate over it then it will be O(N^2) since the function returns a set n with elements of size n.
gradbot
thank you I was not aware of the behaviour of @ - since your sequence one is rather superior I've just deleted mine
ShuggyCoUk
incidentally your functions do have one flaw: try each (1::2::2::3::[])
ShuggyCoUk
let each l = l |> Seq.map (fun x -> x, Seq.filter ((<>) x) l)
The_Ghost
Second function gives 1400 times faster time than the first one.
The_Ghost
If the input elements have no duplicates second function works faster than the fastest solution (DannyAsher's one).
The_Ghost
A: 

This is not much better, if any, than the original solution, but here it goes. This version avoids the list appends by using a utility function to merge the reversed left list with the tail of the right. Also, it uses pattern matching instead of the head and tail functions.

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
dave
+6  A: 

The semantic is slightly different here, but from the example you give Set might be a good fit:

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.
DannyAsher
+1 for adding a printer to the F# interpreter
gradbot
Very elegant solution! But what is the algorithmic complexity of it by your oppinion?
The_Ghost
I believe this should be O(n log n).
DannyAsher
@DannyAsher - I'm pretty sure that it's O(n^2) at best, since you're computing `set xs` each time through the loop. Hoisting that calculation out will make the function *dramatically* faster.
kvb
hi kvb - sorry, I was assuming that `set xs` would be taken out of the loop. The first edit actually has that, but I elided it. Sometimes concision gets the better of me <blush>.
DannyAsher
I think both edits are the same. And the compiler "sees" that xs is same set and will not recalculate it each time (remember the laziness). So I think it'll take anyway O(n log n).May be only a profiler or debugger could prove who's right...
The_Ghost
As an impure language, F# can't know that `set xs` won't result in side effects that you're depending on. As a result, it won't be hoisted automatically by the compiler.
kvb
yeah, by type it does not know, but it could infer internally how it is used. It is just like Intel C++ compiler (very smart compiler) what is doing for C++. "set xs" is using internal type (type it understands well), so the compiler knows what it is doing and that it have no side effects.
The_Ghost
kvb, you're right. The compiler is not so smart and latest correction with "set xs" out of the list comprehension gives a lot faster result. By my profiling it is the fastest and simplest solution so far! It's great how F# compiler work with sets so fast.
The_Ghost
+2  A: 

How about:

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

Or a tail recursive equivalent (producing the lists in reverse order):

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
kvb
Sorry, i re-ran this with a more sane value of 4000 and it runs fine, +1.
gradbot
By the way "as" is a reserved word in the VS 2010 Beta.
gradbot
@gradbot - Thanks, I've changed a,as,b,bs -> x,xs,y,ys to avoid the keyword issue. That's the danger of posting without a compiler handy...
kvb
Your second function is more than two times faster than the first one.
The_Ghost
A: 

Other proposition of mine using Fold. It is linear function O(N). But definitely not so elegant and simple as DannyAsher's solution:

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
The_Ghost
Faster than kvb about minimum 3x for 5000 elements.
The_Ghost