将多个 lambda 函数组合在一起高效执行

发布于 2024-07-17 08:38:53 字数 1014 浏览 2 评论 0原文

我正在尝试使用 lambda 函数的组合创建动态过滤器。 但 lambda 函数的数量可能会根据用户应用的不同过滤器的数量而变化。

我想要这样的行为

//images is a List<ImageInfo>
var result1 = images
.Where(image => image.Bytes < 1000)
.Where(image => image.Height < 100)
.Where(image => image.Width < 100);

,其中高度过滤器仅应用于通过字节过滤器的那些图像。 宽度过滤器仅应用于那些通过高度过滤器的图像。

但是,用户打开和关闭过滤器的动态方式不允许我创建单个 lambda 函数。

相反,我将创建一个 lambda 函数列表,然后需要将它们应用到图像列表。 所以我最终会得到这样的结果; 多个单独的 lambda 函数。

var filter1 = images.Where(image => image.Bytes < 1000);
var filter2 = images.Where(image => image.Height < 100);
var filter3 = images.Where(image => image.Width < 100);

如何将多个 lambda 函数连接在一起以获得过滤图像的最终列表?

我这样做了,

var result = filter1.Intersect<ImageInfo>(filter2).Intersect<ImageInfo>(filter3);

但是每个过滤器都会旋转主图像列表以获取其图像子集,然后进行交集计算,这会占用太多 CPU。

因此,我正在寻找一种方法来获取任意 lambda 函数列表(表达式...无论什么),并以我展示的第一个示例的方式执行它们。

I'm trying to create a dynamic filter using a combination of lambda functions. But the number of lambda functions could vary based on the number of different filters the user applies.

I want something that behaves like this

//images is a List<ImageInfo>
var result1 = images
.Where(image => image.Bytes < 1000)
.Where(image => image.Height < 100)
.Where(image => image.Width < 100);

Where the height filter is only applied to those images that pass the bytes filter. And the width filter is only applied by to those images that pass the height filter.

But, the dynamic fashion of how the filters are turned on and off by the users doesnt allow me to create a single lambda function.

Instead I'll create a list of lambda functions and then need to apply them to the list of images. So I would end up with something like this; multiple individual lambda functions.

var filter1 = images.Where(image => image.Bytes < 1000);
var filter2 = images.Where(image => image.Height < 100);
var filter3 = images.Where(image => image.Width < 100);

How can I join multiple lambda functions together to get my final list of filtered images?

I did this

var result = filter1.Intersect<ImageInfo>(filter2).Intersect<ImageInfo>(filter3);

But each filter spins through the main list of images to get its subset of images, and then does an intersection calculation, which takes way too much CPU.

So what I'm looking for is a way to take an arbitrary list of lambda functions (Expressions...whatever) and join them in a way that gets executed the way the first example I showed does.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

郁金香雨 2024-07-24 08:38:53

好吧,怎么样:

static Func<T, bool> CombineWithAnd<T>(IEnumerable<Func<T, bool>> filters)
{
    return x =>
    {
        foreach (var filter in filters)
        {
            if (!filter(x))
            {
                return false;
            }
        }
        return true;
    };
}

这就是你想要的吗? 基本上,它捕获 lambda 表达式中的一组过滤器,并在使用返回的 lambda 时依次应用它们(短路)。 显然,您也可以以“或”的方式执行相同的操作:

static Func<T, bool> CombineWithOr<T>(IEnumerable<Func<T, bool>> filters)
{
    return x =>
    {
        foreach (var filter in filters)
        {
            if (filter(x))
            {
                return true;
            }
        }
        return false;
    };
}

请注意,如果在调用该方法后修改了过滤器集合(例如,它是一个 List 并且您添加了一个新过滤器),则返回的“组合”过滤器将反映这些更改。 如果您不希望出现这种行为,请添加:

filters = filters.ToList();

作为方法的第一行,以有效地获取副本。 请注意,委托是不可变的,因此您无需担心它们会发生变化。

Okay, how about:

static Func<T, bool> CombineWithAnd<T>(IEnumerable<Func<T, bool>> filters)
{
    return x =>
    {
        foreach (var filter in filters)
        {
            if (!filter(x))
            {
                return false;
            }
        }
        return true;
    };
}

Is that what you're after? Basically that captures the set of filters within the lambda expression, and applies them one after another (with short-circuiting) when the returned lambda is used. Obviously you could do the same in an "Or" fashion too:

static Func<T, bool> CombineWithOr<T>(IEnumerable<Func<T, bool>> filters)
{
    return x =>
    {
        foreach (var filter in filters)
        {
            if (filter(x))
            {
                return true;
            }
        }
        return false;
    };
}

Note that if the collection of filters is modified after calling the method (e.g. it's a List<T> and you add a new filter) then the returned "combination" filter will reflect those changes. If you don't want that behaviour, add:

filters = filters.ToList();

as the first line of the method, to effectively take a copy. Note that delegates are immutable, so you don't need to worry about them changing.

指尖上的星空 2024-07-24 08:38:53

哦太好了! 你非常接近,但足够接近让我得到答案。 要应用所有过滤器,return true 需要移出 foreach 循环(如下所示)。 但是,是的,这正是我正在寻找的。

一个问题,或者评论。 这个函数真正让我震惊的是 x 变量。 我必须运行并调试代码才能确定 x 的类型为 。 我以前从未见过没有类型或 var 声明的变量,这确实让我感到震惊。 您能解释一下允许变量 x 不进行任何类型声明的 C# 规则吗?

顺便说一句,非常优雅的解决方案。

static Func<T, bool> CombineWithAnd<T>(IEnumerable<Func<T, bool>> filters)
{
    return x =>
    {
        foreach (var filter in filters)
        {
            if (!filter(x))
            {
                return false;
            }
        }
        return true;
    };
}

Oh sweet! You were very close, but close enough the get me the answer. To apply all filters, the return true needs to move outside the foreach loop (shown below). But yes, that's exactly what I'm looking for.

One question, or comment. What really threw me about this function was the x variable. I had to run and debug the code to figure out that the x was going to be of type <T>. I've never seen a variable that didn't have a type or var declaration before and that really threw me. Can you explain a bit the C# rules for allowing the variable x without any kind of declaration?

Very elegant solution by the way.

static Func<T, bool> CombineWithAnd<T>(IEnumerable<Func<T, bool>> filters)
{
    return x =>
    {
        foreach (var filter in filters)
        {
            if (!filter(x))
            {
                return false;
            }
        }
        return true;
    };
}
许仙没带伞 2024-07-24 08:38:53

为什么不像第一个示例那样一个接一个地应用这些函数呢?

您的过滤器具有签名,

Func<IEnumerable<ImageInfo>, IEnumerable<ImageInfo>> 

因此只需将每个过滤器应用于最后一个过滤器的结果即可?

像这样?

IEnumerable<ImageInfo> filtered = images;

if(filterByBytes)
    filtered = filtered.Where(image => image.Bytes < 1000);

if(filterByHeight)
    filtered = filtered.Where(image => image.Height < 100);

if(filterByWidth)
    filtered = filtered.Where(image => image.Width < 100);

编辑回复:评论,从我的脑海中浮现出来,就像......

List<Func<IEnumerable<ImageInfo>, IEnumerable<ImageInfo>>> lambdas = new List<Func<IEnumerable<ImageInfo>, IEnumerable<ImageInfo>>>();

lambdas.add(x => x.Where(image => image.Bytes < 1000));
lambdas.add(x => x.Where(image => image.Height < 100));
lambdas.add(x => x.Where(image => image.Width < 100));

foreach(var lambda in lambdas)
    images = lamdba.Invoke(images);

why don't you just apply the functions one after the other, as in the first example?

Youre filters have the signature of

Func<IEnumerable<ImageInfo>, IEnumerable<ImageInfo>> 

so just apply each filter to the result of the last one?

like this?

IEnumerable<ImageInfo> filtered = images;

if(filterByBytes)
    filtered = filtered.Where(image => image.Bytes < 1000);

if(filterByHeight)
    filtered = filtered.Where(image => image.Height < 100);

if(filterByWidth)
    filtered = filtered.Where(image => image.Width < 100);

Edit re: comments, off the top of my head, something like...

List<Func<IEnumerable<ImageInfo>, IEnumerable<ImageInfo>>> lambdas = new List<Func<IEnumerable<ImageInfo>, IEnumerable<ImageInfo>>>();

lambdas.add(x => x.Where(image => image.Bytes < 1000));
lambdas.add(x => x.Where(image => image.Height < 100));
lambdas.add(x => x.Where(image => image.Width < 100));

foreach(var lambda in lambdas)
    images = lamdba.Invoke(images);
当梦初醒 2024-07-24 08:38:53

就其价值而言,您不需要使用循环。 (在 Python 中,因为我没有使用过 C#:)

def conjoin(filter1, filter2):
    return lambda x: filter1(x) and filter2(x)

def conjoin_list(filters):
    return reduce(conjoin, filters)

(相当于C#中的reduce函数,在那里称为fold。)

For what it's worth, you needn't use a loop. (In Python since I haven't used C#:)

def conjoin(filter1, filter2):
    return lambda x: filter1(x) and filter2(x)

def conjoin_list(filters):
    return reduce(conjoin, filters)

(Equivalent of the reduce function in C#, called fold there.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文