F#函数将n维列表转换为1维列表

时间:2010-07-08 20:06:56

标签: f#

我在F#中尝试了一些小任务来帮助掌握语言。

我想编写一个带有n维列表的函数,并返回包含每个维度中所有元素的1维列表。

例如,如果输入是以下三维列表:[[[1; 2]; [3; 4]]; [[5; 6]; [7; 8]]],输出将是是:[1; 2; 3; 4; 5; 6; 7; 8]

对于二维 - > 1维该功能非常简单:

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

我试图将此概括为n维:

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

当我尝试编译时出现以下错误:

错误FS0001:类型不匹配。期待着 '列表清单
但是给了一个 '列表
统一''a'和''列表'

时,结果类型将是无限的

问题在于:

List.collect(fun item -> item) list

我的想法显然有问题。编写这种函数的正确方法是什么?

5 个答案:

答案 0 :(得分:3)

此操作的输入不是很好,但这是一个适用于IEnumerable并返回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))

你也可以写

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
    ]

更通用,例如

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

修改

@Jon Harrop提出了另一种策略 - 为嵌套列表创建一个新类型:

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) 

答案 1 :(得分:2)

F#型系统无法表达这一点。最常见的解决方案是创建一个表示嵌套列表的新类型。

答案 2 :(得分:1)

抱歉,我对F#语法有一点了解,这是C#中的解决方案,可能会有所帮助:

namespace FlatEnumerable
{
    class Program
    {
        static void Main(string[] args)
        {
            var arr = new int[,,] { { { 1, 2 }, { 3, 4 } }, { { 5, 6 }, { 7, 8 } } };
            foreach (var i in arr.Flat())
                Console.WriteLine(i);
        }
    }

    static class Enumerable2
    {
        public static IEnumerable Flat(this IEnumerable source)
        {
            foreach (var item in source)
            {
                var enu = item as IEnumerable;
                if (enu != null)
                    foreach (var c in enu.Flat())
                        yield return c;
                else
                    yield return item;
            }
        }
    }
}

我相信它可以通过使用模式匹配而不是强制转换并检查null来改进F#。

答案 3 :(得分:0)

不,问题是coalesce获取列表列表,编译器不知道List.collect(fun item -> item) list总是返回列表列表(事实上编译器无法知道)那是因为它不是真的)。但是,由于这是你传递给coalesce的参数,编译器需要知道为了允许该调用。

答案 4 :(得分:0)

我采用嵌套List的想法,并尝试编写一个更整洁的版本:

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

let nested = [L[L[V 1;V 2]; V 3]; V 4; L[L[V 5;V 6]; V 7]] 

let flatten n1 = 
            let rec traverseNestedList nl = match nl with      
                                            | V c -> [c]           
                                            | L a -> List.collect traverseNestedList a
            List.collect traverseNestedList n1 

let res7 = nested |> flatten