我在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
我的想法显然有问题。编写这种函数的正确方法是什么?
答案 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