Skip to content

Instantly share code, notes, and snippets.

@sonnemaf
Last active November 27, 2019 12:44
Show Gist options
  • Save sonnemaf/8853274ff83b4fe8b96a070200aa8b2a to your computer and use it in GitHub Desktop.
Save sonnemaf/8853274ff83b4fe8b96a070200aa8b2a to your computer and use it in GitHub Desktop.
using System;
using System.Runtime.InteropServices;
namespace Linq4Span {
// QUESTION: Does LINQ for ReadOnlySpan<T> make sense or is the extra Closure class a showstopper?
// QUESTION: Should it only support ReadOnlySpan<T> and not Span<T>
// QUESTION: What about Memory<T> and ReadonlyMemory<T>
class Program {
static void Main(string[] args) {
ReadOnlySpan<int> numbers = new int[] { 1, 2, 3, 4, 5, 6, 7 };
var qry = numbers.Where((in int n) => n % 2 == 0);
foreach (var item in qry) {
Console.WriteLine(item);
}
// 2, 4, 6
Console.WriteLine(numbers.Sum()); // 28
Console.WriteLine(numbers.Where((in int n) => n % 2 == 0).Sum()); // 12
Console.WriteLine(numbers.Where((in int n) => n % 2 == 0).Count()); // 3
Console.WriteLine(numbers.Count((in int n) => n % 2 == 0)); // 3
ReadOnlySpan<PointStruct> points = new PointStruct[] {
new PointStruct(1,1),
new PointStruct(2,2),
new PointStruct(3,3),
};
Console.WriteLine(points.Where((in PointStruct p) => p.X > 1).Sum((in PointStruct p) => ref p.X)); // 5 (2 + 3)
}
}
static class SpanExtensions {
// QUESTION: Should we have seperate delegates with IN parameters and REF READONLY returns
public delegate bool Predicate<T>(T value);
public delegate bool PredicateWithIn<T>(in T value);
public delegate ref readonly TResult RefReadonlySelector<TValue, TResult>(in TValue value);
public delegate TResult Selector<TValue, TResult>(in TValue value);
public static FilterEnumerable<T> Where<T>(this ReadOnlySpan<T> source, PredicateWithIn<T> predicate) {
return new FilterEnumerable<T>(source, predicate);
}
// TODO: Overload without IN
//public static FilterEnumerable<T> Where<T>(this Span<T> source, Predicate<T> predicate) {
// return new FilterEnumerable<T>(source, predicate);
//}
// TODO: Overloads for double, decimal, long etc
public static int Sum(this ReadOnlySpan<int> source) {
int total = 0;
foreach (ref readonly var item in source) {
total += item;
}
return total;
}
// TODO: Overloads for double, decimal, long etc
public static int Sum(this FilterEnumerable<int> source) {
int total = 0;
foreach (ref readonly var item in source) {
total += item;
}
return total;
}
// TODO: Overloads for double, decimal, long etc
public static int Sum<T>(this FilterEnumerable<T> source, RefReadonlySelector<T, int> selector) {
int total = 0;
foreach (ref readonly var item in source) {
total += selector(item);
}
return total;
}
// TODO: Overloads for double, decimal, long etc
public static int Sum<T>(this FilterEnumerable<T> source, Selector<T, int> selector) {
int total = 0;
foreach (ref readonly var item in source) {
total += selector(item);
}
return total;
}
public static int Count<T>(this FilterEnumerable<T> source) {
int index = 0;
foreach (ref readonly var item in source) {
index += 1;
}
return index;
}
public static int Count<T>(this ReadOnlySpan<T> source, PredicateWithIn<T> predicate) {
int index = 0;
foreach (ref readonly var item in source.Where(predicate)) {
index += 1;
}
return index;
}
public ref struct FilterEnumerable<T> {
private readonly ReadOnlySpan<T> _source;
private readonly PredicateWithIn<T> _predicate;
public FilterEnumerable(ReadOnlySpan<T> source, PredicateWithIn<T> predicate) {
this._source = source;
this._predicate = predicate;
}
public FilterEnumerator<T> GetEnumerator() {
return new FilterEnumerator<T>(_source, _predicate);
}
}
public ref struct FilterEnumerator<T> {
private readonly ReadOnlySpan<T> _source;
private readonly PredicateWithIn<T> _predicate;
private int _index;
public FilterEnumerator(ReadOnlySpan<T> source, PredicateWithIn<T> predicate) : this() {
this._source = source;
this._predicate = predicate;
this._index = -1;
}
public ref readonly T Current => ref _source[_index];
public bool MoveNext() {
while (_index++ < _source.Length - 1) {
if (_predicate(Current)) {
return true;
}
}
return false;
}
}
}
readonly struct PointStruct {
public readonly int X; // { get; set; }
public readonly int Y; // { get; set; }
public PointStruct(int x, int y) {
this.X = x;
this.Y = y;
}
public double Dist => Math.Sqrt((X * X) + (Y * Y));
public override string ToString() => $"({X.ToString()},{Y.ToString()})";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment