Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# function to convert a n-dimensional list to 1-dimensional list

Tags:

f#

I'm attempting a few minor tasks in F# to help get a handle on the language.

I would like to write a function that takes a n-dimensional list and returns a 1-dimensional list containing all the elements from each dimension.

For example, if the input was the following 3-dimensional list: [[[1;2];[3;4]];[[5;6];[7;8]]], the output would be: [1;2;3;4;5;6;7;8]

For 2-dimensions -> 1-dimension the function is pretty straightforward:

let coalesce list= List.collect(fun item -> item) list

Here is my attempt to generalize this to n-dimensions:

let rec coalesce (list, dimension) = 
    if dimension = 1 then list 
    else coalesce (List.collect(fun item -> item) list, dimension - 1)

I get the following error when I try to compile:

error FS0001: Type mismatch. Expecting a
'a list list
but given a
'a list
The resulting type would be infinite when unifying ''a' and ''a list'

The issue is here:

List.collect(fun item -> item) list

There's obviously something wrong with my thinking. What's the proper way to write this sort of function?

like image 928
Narwe Avatar asked Dec 06 '25 00:12

Narwe


1 Answers

This operation is not well-typed, but here's a sample that works on IEnumerables and returns a list<obj>:

let rec coalesce(list:System.Collections.IEnumerable, dim) =
    [
        if dim=1 then for x in list do yield x
        else
            for x in list do
                match x with
                | :? System.Collections.IEnumerable as s ->
                    yield! coalesce(s, dim-1)
                | _ -> failwith "bad shape"
    ]
printfn "%A" (coalesce([1;2], 1))
printfn "%A" (coalesce([[1;2];[3;4]], 2))
printfn "%A" (coalesce([[[1;2];[3;4]];[[5;6];[7]]], 3))

You can also write

let rec flatten(list:System.Collections.IEnumerable) =
    [for x in list do
        match x with
        | :? System.Collections.IEnumerable as s -> yield! flatten(s)
        | _ -> yield x
    ]

which is more general, e.g.

let weird : obj list = [[box [1;2]; box 3]; 4; [box [5;6]; box 7]]
printfn "%A" (flatten weird)

EDIT

@Jon Harrop suggested another strategy - create a new type for nested lists:

type NestedListElement<'T> = //'
    | L of NestedListElement<'T> list //'
    | V of 'T //'

let rec flatten nlist = 
    [for x in nlist do 
        match x with 
        | L l -> yield! flatten l
        | V v -> yield v
    ] 

let nested = [L[L[V 1;V 2]; V 3]; V 4; L[L[V 5;V 6]; V 7]] 
printfn "%A" (flatten nested) 
like image 116
Brian Avatar answered Dec 09 '25 00:12

Brian



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!