使用最后一个元素扩展列表以匹配 LINQ zip 中更长的列表
Extend a list using last element to match longer list in LINQ zip
我有两个合集:
p = [ a, b ]
v = [ 1, 2, 3, 4, 5 ]
我想处理以下元组:
[ [a,1], [b,2], [b,3], [b,4], [b,5] ]
我当前的代码是:
p.zip(v, (x,y) => { ... })
当然我最终只处理了:
[ [a,1], [b,2] ]
感觉我想创建一个 last(p) 的无限列表,由 p 覆盖,然后我们用 v 压缩它。类似于:
extend(p).zip( v, ... )
在哪里扩展(IEnumerable 列表)returns
[ a, b, b, b, ..., b ] // Infinite/generated sequence
我想我可以将 extend() 编写为列表上的枚举器,当用尽时,它会无限返回最后一个元素。
我感兴趣的是,是否存在通过组合现有函数来创建扩展的函数式方法。我很高兴看到它以 Haskell/F# 表示,即使这些函数在 LINQ 中不存在。我鼓励自己从功能上思考。
我查看了 https://hackage.haskell.org/package/base-4.2.0.1/docs/Data-List.html 可能的原子,但没有看到任何可能创造我需要的东西。
谢谢!
编辑:
我进一步减少了问题:
extend(list) = list.Concat( repeat(list.Last()) )
where repeat(T item) returns [ item, item, ..., item ]
我现在正在搜索 LINQ 和 Haskell 以寻找 "repeat".
之类的现有实现
在Haskell (ghci)
> let p = ["A", "B", "C"]
> let v = [1..]
> let zipExtending a = zip (a ++ repeat (last a))
然后
> mapM_ print $ take 5 $ zipExtending p v
("A",1)
("B",2)
("C",3)
("C",4)
("C",5)
>
您可以使用 Linq 创建扩展
public static class Extensions {
public static IEnumerable<A> extend<A>(this IEnumerable<A> a, int count) {
return a.Concat(Enumerable.Range(0, Math.Max(0, count - a.Count())).Select(_ => a.Last()));
}
public static IEnumerable<Tuple<A, B>> zipExtendingBoth<A, B>(this IEnumerable<A> a, IEnumerable<B> b) {
if(!a.Any() || !b.Any())
return new Tuple<A, B> [] { };
return a.extend(b.Count()).Zip(b.extend(a.Count()), Tuple.Create);
}
}
然后
var p = new string [] { "A", "B", "C" };
var v = Enumerable.Range(1, 5);
foreach(var e in p.zipExtendingBoth(v))
Console.Write("{0}, {1}\n", e.Item1, e.Item2);
结果
A, 1
B, 2
C, 3
C, 4
C, 5
如果您的 "right" 列表是无限的(首先不能)那么
public static class Extensions {
public static IEnumerable<Tuple<A, B>> zipWithInfinite<A, B>(this IEnumerable<A> a, IEnumerable<B> b) {
if(!a.Any() || !b.Any())
return new Tuple<A, B> [] { };
var max_a_index = a.Count() - 1;
return b.Select((x, i) => Tuple.Create(a.ElementAt(Math.Min(i, max_a_index)), x));
}
}
您提到的功能repeat
可以实现如下:
let repeat item =
Seq.initInfinite (fun _ -> item)
留在.Net/C#世界:
您可以使用类似的东西:
var almostInfinite = items.Concat(Enumerable.Repeat(items.Last(), Int32.MaxValue));
但它不会产生真正无限的序列。
编写自己的 Extend
方法也不难:
IEnumerable<T> Extend<T>(IEnumerable<T> source)
{
// error checking omitted
var e = source.GetEnumerator();
T last = default(T);
while(e.MoveNext())
yield return last = e.Current;
while(true)
yield return last;
}
您还可以创建另一个版本的 Zip
。看看 morelinq 的 Zip
,它处理不同大小的源序列。
Haskell中的解决方案:
> let extend = foldr (\x xs -> x : if null xs then repeat x else xs) []
一些测试:
> extend []
[]
> take 10 $ extend [1]
[1,1,1,1,1,1,1,1,1,1]
> take 10 $ extend [1,2,3]
[1,2,3,3,3,3,3,3,3,3]
上面没有使用last
,所以我们不持有对extend
的整个输入列表的引用——这样它就可以被垃圾收集。
当然,显式递归也可以:
extend :: [a] -> [a]
extend [] = []
extend [x] = repeat x
extend (x:xs) = x : extend xs
我有两个合集:
p = [ a, b ]
v = [ 1, 2, 3, 4, 5 ]
我想处理以下元组:
[ [a,1], [b,2], [b,3], [b,4], [b,5] ]
我当前的代码是:
p.zip(v, (x,y) => { ... })
当然我最终只处理了:
[ [a,1], [b,2] ]
感觉我想创建一个 last(p) 的无限列表,由 p 覆盖,然后我们用 v 压缩它。类似于:
extend(p).zip( v, ... )
在哪里扩展(IEnumerable 列表)returns
[ a, b, b, b, ..., b ] // Infinite/generated sequence
我想我可以将 extend() 编写为列表上的枚举器,当用尽时,它会无限返回最后一个元素。
我感兴趣的是,是否存在通过组合现有函数来创建扩展的函数式方法。我很高兴看到它以 Haskell/F# 表示,即使这些函数在 LINQ 中不存在。我鼓励自己从功能上思考。
我查看了 https://hackage.haskell.org/package/base-4.2.0.1/docs/Data-List.html 可能的原子,但没有看到任何可能创造我需要的东西。
谢谢!
编辑:
我进一步减少了问题:
extend(list) = list.Concat( repeat(list.Last()) )
where repeat(T item) returns [ item, item, ..., item ]
我现在正在搜索 LINQ 和 Haskell 以寻找 "repeat".
之类的现有实现在Haskell (ghci)
> let p = ["A", "B", "C"]
> let v = [1..]
> let zipExtending a = zip (a ++ repeat (last a))
然后
> mapM_ print $ take 5 $ zipExtending p v
("A",1)
("B",2)
("C",3)
("C",4)
("C",5)
>
您可以使用 Linq 创建扩展
public static class Extensions {
public static IEnumerable<A> extend<A>(this IEnumerable<A> a, int count) {
return a.Concat(Enumerable.Range(0, Math.Max(0, count - a.Count())).Select(_ => a.Last()));
}
public static IEnumerable<Tuple<A, B>> zipExtendingBoth<A, B>(this IEnumerable<A> a, IEnumerable<B> b) {
if(!a.Any() || !b.Any())
return new Tuple<A, B> [] { };
return a.extend(b.Count()).Zip(b.extend(a.Count()), Tuple.Create);
}
}
然后
var p = new string [] { "A", "B", "C" };
var v = Enumerable.Range(1, 5);
foreach(var e in p.zipExtendingBoth(v))
Console.Write("{0}, {1}\n", e.Item1, e.Item2);
结果
A, 1
B, 2
C, 3
C, 4
C, 5
如果您的 "right" 列表是无限的(首先不能)那么
public static class Extensions {
public static IEnumerable<Tuple<A, B>> zipWithInfinite<A, B>(this IEnumerable<A> a, IEnumerable<B> b) {
if(!a.Any() || !b.Any())
return new Tuple<A, B> [] { };
var max_a_index = a.Count() - 1;
return b.Select((x, i) => Tuple.Create(a.ElementAt(Math.Min(i, max_a_index)), x));
}
}
您提到的功能repeat
可以实现如下:
let repeat item =
Seq.initInfinite (fun _ -> item)
留在.Net/C#世界:
您可以使用类似的东西:
var almostInfinite = items.Concat(Enumerable.Repeat(items.Last(), Int32.MaxValue));
但它不会产生真正无限的序列。
编写自己的 Extend
方法也不难:
IEnumerable<T> Extend<T>(IEnumerable<T> source)
{
// error checking omitted
var e = source.GetEnumerator();
T last = default(T);
while(e.MoveNext())
yield return last = e.Current;
while(true)
yield return last;
}
您还可以创建另一个版本的 Zip
。看看 morelinq 的 Zip
,它处理不同大小的源序列。
Haskell中的解决方案:
> let extend = foldr (\x xs -> x : if null xs then repeat x else xs) []
一些测试:
> extend []
[]
> take 10 $ extend [1]
[1,1,1,1,1,1,1,1,1,1]
> take 10 $ extend [1,2,3]
[1,2,3,3,3,3,3,3,3,3]
上面没有使用last
,所以我们不持有对extend
的整个输入列表的引用——这样它就可以被垃圾收集。
当然,显式递归也可以:
extend :: [a] -> [a]
extend [] = []
extend [x] = repeat x
extend (x:xs) = x : extend xs