在C#中等同于Haskell';s Data.List.Span

在C#中等同于Haskell';s Data.List.Span,c#,linq,haskell,C#,Linq,Haskell,您好,是否已经实现了任何有效的方法来获取HaskellData.List.span的功能 span :: (a -> Bool) -> [a] -> ([a], [a]) 基本上给定了一个列表和一个谓词,我想在第一个错误谓词出现后将列表一分为二。测试错误的pivot元素后面的元素可能尊重谓词,也可能不尊重谓词,但我不在乎 List: [1,2,3,1,2,3] Predicate: x<3 Span: `span (x<3) [1,2,3,1,2,3]`

您好,是否已经实现了任何有效的方法来获取Haskell
Data.List.span的功能

span :: (a -> Bool) -> [a] -> ([a], [a])
基本上给定了一个
列表和一个
谓词
,我想在第一个错误谓词出现后将列表一分为二。测试
错误的
pivot
元素后面的元素可能尊重谓词,也可能不尊重谓词,但我不在乎

List: [1,2,3,1,2,3]
Predicate: x<3
Span:  `span  (x<3) [1,2,3,1,2,3]`   =>  `([1,2],[3,1,2,3])`
列表:[1,2,3,1,2,3]

谓词:x我相信你在寻找一个c#IEnumerable。你可以写一个例子

IEnumerable<int> list = new List<int> { 1,2,3,4,5,6};
var list1 = list.Where(x=>x>3); //deferred execution
var list2 = list.Where(x=>x<=3); //deferred execution
IEnumerable list=新列表{1,2,3,4,5,6};
var list1=list.Where(x=>x>3)//延迟执行
var list2=list.Where(x=>x最简单的使用方法

示例

var listInt = new List<int>{1, 2, 3, 4, 5, 6};
var result = listInt.ToLookup(x => x > 3);
var listInt=新列表{1,2,3,4,5,6};
var result=listInt.ToLookup(x=>x>3);
结果

var listInt = new List<int> { 1, 2, 3, 1, 2, 3 };
[1,2,3],[4,5,6]]

编辑

var listInt = new List<int> { 1, 2, 3, 1, 2, 3 };
var listInt=新列表{1,2,3,1,2,3};
创建一个扩展方法

        public static IEnumerable<T> TakeUntil<T>(this IEnumerable<T> source, Func<T, bool> predicate)
        {
            foreach (var item in source)
            {
                if (!predicate(item))
                    break;
                yield return item;
            }
        }
public静态IEnumerable TakeUntil(此IEnumerable源,Func谓词)
{
foreach(源中的var项)
{
if(!谓词(项))
打破
收益回报项目;
}
}
叫它

var first = listInt.TakeUntil(x => x < 3);
var second = listInt.Skip(first.Count());
var first=listInt.TakeUntil(x=>x<3);
var second=listInt.Skip(first.Count());
结果

var listInt = new List<int> { 1, 2, 3, 1, 2, 3 };
第一个=[1,2]


second=[3,1,2,3]

您可以使用
TakeWhile
Skip

public static IEnumerable<IEnumerable<T>> SplitWhen<T>(this IEnumerable<T> enumerable, Func<T, bool> predicate)
{
    var first = enumerable.TakeWhile(predicate);
    yield return first;
    var second = enumerable.Skip(first.Count());
    yield return second;
}
public静态IEnumerable SplitWhen(此IEnumerable可枚举,Func谓词)
{
var first=enumerable.TakeWhile(谓词);
收益率优先;
var second=enumerable.Skip(first.Count());
收益率第二;
}

更新

var listInt = new List<int>{1, 2, 3, 4, 5, 6};
var result = listInt.ToLookup(x => x > 3);
要避免多次迭代,并且不需要使用列表或数组,请执行以下操作:

public static IEnumerable<IEnumerable<T>> SplitWhen<T>(this IEnumerable<T> enumerable, Func<T, bool> predicate)
{
    yield return enumerable.TakeWhile(predicate);
    yield return enumerable.TakeAfter(predicate);
}

public static IEnumerable<T> TakeAfter<T>(this IEnumerable<T> enumerable, Func<T, bool> predicate)
{
    bool yielding = false;
    foreach (T item in enumerable)
    {
        if (yielding = yielding || !predicate(item))
        {
            yield return item;
        }
    }
}
public静态IEnumerable SplitWhen(此IEnumerable可枚举,Func谓词)
{
产生返回可枚举.TakeWhile(谓词);
产生返回可枚举.TakeAfter(谓词);
}
公共静态IEnumerable TakeAfter(此IEnumerable可枚举,Func谓词)
{
布尔=假;
foreach(可枚举中的T项)
{
if(屈服=屈服| |!谓词(项))
{
收益回报项目;
}
}
}

我认为没有一个本机的.NET Framework或.NET Core方法可以做到这一点,因此您可能需要编写自己的方法。以下是我的扩展方法实现:

public static Tuple SplitWhen(此IEnumerable self,Func-Func)
{
//将self枚举到一个数组中,这样就不会重复多次
var enumerable=作为T[]的self.ToArray();
var matching=enumerable.TakeWhile(func.ToArray();
var notMatching=enumerable.Skip(matching.Length);
返回新元组(匹配,不匹配);
}
此方法将返回一个元组,其中
tuple.Item1
是列表中与谓词匹配的部分,而
tuple.Item2
是列表的其余部分


此方法需要在一个单独的静态类中声明,因为它是
IEnumerable
的扩展方法。如果您想命名
Item1
Item2
一些不同的东西,也可以使用它。如果您喜欢使用列表,那么您可以通过一次传递源列表来创建两个新列表,如因此:

public static (List<T> part1, List<T> part2) SplitListBy<T>(List<T> source, Predicate<T> splitWhen)
{
    var part1 = new List<T>();

    int i;

    for (i = 0; i < source.Count && !splitWhen(source[i]); ++i)
        part1.Add(source[i]);

    var part2 = source.GetRange(i, source.Count - i);

    return (part1, part2);
}
输出:

1, 2
3, 1, 2, 3

如果您不需要两个新列表,但只想对一个零件使用原始列表,对另一个零件使用单个新列表,则可以这样做:

public static List<T> SplitListBy<T>(List<T> source, Predicate<T> splitWhen)
{
    int i;

    for (i = 0; i < source.Count && !splitWhen(source[i]); ++i)
        ;

    var part2 = source.GetRange(i, source.Count - i);

    source.RemoveRange(i, source.Count - i);

    return part2;
}
公共静态列表SplitListBy(列表源,谓词splitWhen)
{
int i;
对于(i=0;i
这方面的测试代码非常相似:

var list = new List<int>{ 1, 2, 3, 1, 2, 3 };

var part2 = SplitListBy(list, item => item >= 3);

Console.WriteLine(string.Join(", ", list));
Console.WriteLine(string.Join(", ", part2));
var list=新列表{1,2,3,1,2,3};
var part2=SplitListBy(列表,项=>项>=3);
Console.WriteLine(string.Join(“,”,list));
Console.WriteLine(string.Join(“,”,part2));

(输出与其他测试代码相同。)

我已经接受了@Matthew Watson解决方案。不过我也将使用
Span
ReadOnlyMemory发布一些修改过的版本

public static (IEnumerable<T>first,IEnumerable<T> second) Span<T>(this ReadOnlyMemory<T> original,Func<T,bool> predicate) {
            List<T> list = new List<T>();

            int splitIndex = 0;
            for (int i = 0; i < original.Length && !predicate(original.Span[i]); i++) {
                list.Add(original.Span[splitIndex=i]);
            }
            var part2 = original.Slice(splitIndex);
            return (list, part2.ToArray());
        }
public static(IEnumerablefirst,IEnumerable second)Span(此ReadOnlyMemory原始,Func谓词){
列表=新列表();
int-splitIndex=0;
for(int i=0;i
在我写这个答案的时候,我不认为其他任何答案都忠实地复制了Haskell的
span
函数。没关系,你可能真的在寻找其他东西,但为了完成,我想添加这个

首先,你不能假设 StAs只对输入列表进行一次迭代。很难对Haskell的运行时行为进行懒惰的评估,但是请考虑这个列表:

xs = [trace "one" 1, trace "two" 2, trace "three" 3,
      trace "one" 1, trace "two" 2, trace "three" 3]
在这里,我特意使用了
Debug.trace
中的
trace
,这样我们就可以观察发生了什么。特别是,我想让您注意一下,如果您独立地迭代列表,会发生什么,就像在“真实”代码中可能会发生的那样:

Prelude Data.List Debug.Trace> (l, r) = span (< 3) xs
Prelude Data.List Debug.Trace> l
one
[1two
,2three
]
请注意,虽然它只打印
[3,1,2,3]
,但它会在整个列表中进行迭代。否则它怎么会这样做呢?它是一个函数。它不会在已经迭代列表的距离上保留书签

另一方面,该函数处理无限列表:

Prelude Data.List> take 10 $ fst $ span (< 3) $ repeat 1
[1,1,1,1,1,1,1,1,1,1]
Prelude Data.List> take 10 $ fst $ span (< 3) $ repeat 3
[]
Prelude Data.List> take 10 $ snd $ span (< 3) $ repeat 3
[3,3,3,3,3,3,3,3,3,3]

只是想澄清一下:您正在寻找一种
> var (left, right) = new[] { 1, 2, 3, 1, 2, 3 }.Span(x => x < 3);
> left
TakeWhileIterator { 1, 2 }
> right
SkipWhileIterator { 3, 1, 2, 3 }
> var (left, right) = 1.RepeatInfinite().Span(x => x < 3);
> left.Take(10)
TakeIterator { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
> var (left, right) = 3.RepeatInfinite().Span(x => x < 3);
> right.Take(10)
TakeIterator { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }
> left.Take(10)
TakeIterator { }