为什么SelectMany需要M <s> - &gt; (S - > M c) - &gt; (S - > M c - > R) - > Ë<R>?

时间:2017-06-05 04:01:24

标签: c# monads

在链接https://msdn.microsoft.com/en-us/library/bb534631(v=vs.110).aspx中,第三个签名是

// M<S> -> (S -> M<C>) -> (S -> M<C> -> R) -> E<R>
public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, IEnumerable<TCollection>> collectionSelector,
    Func<TSource, TCollection, TResult> resultSelector
)

不应该是第一个具有典型Monad绑定函数M<A> -> (A -> M<B>) -> M<B>签名的人吗?是不是很容易将resultSelector中的代码合并到collectionSelector

MSDN文档举例说明了该方法的用法。

class Program
{
    static void Main(string[] args)
    {
        SelectManyEx3();
    }
    public static void SelectManyEx3()
    {
        PetOwner[] petOwners =
            { new PetOwner { Name="Higa",
                    Pets = new List<string>{ "Scruffy", "Sam" } },
                new PetOwner { Name="Ashkenazi",
                    Pets = new List<string>{ "Walker", "Sugar" } },
                new PetOwner { Name="Price",
                    Pets = new List<string>{ "Scratches", "Diesel" } },
                new PetOwner { Name="Hines",
                    Pets = new List<string>{ "Dusty" } } };

        // Project the pet owner's name and the pet's name.
        var query =
            petOwners
            .SelectMany(petOwner => petOwner.Pets, (petOwner, petName) => new { petOwner, petName })
            .Where(ownerAndPet => ownerAndPet.petName.StartsWith("S"))
            .Select(ownerAndPet =>
                    new
                    {
                        Owner = ownerAndPet.petOwner.Name,
                        Pet = ownerAndPet.petName
                    }
            );

        // Print the results.
        foreach (var obj in query1)
        {
            Console.WriteLine(obj);
        }
    }
}

class PetOwner
{
    public string Name { get; set; }
    public List<string> Pets { get; set; }
}

但是,可以使用第一个签名的var query = ...重写SelectMany,如下所示?

var query = petOwners.SelectMany(o => o.Pets.Select(p => new { petOwner = o, petName =p}))

具有第三个签名的SelectMany何时才真正有用?

2 个答案:

答案 0 :(得分:0)

使用附加“结果选择器”参数的SelectMany()方法的第3次和第4次重载需要更多解释。

额外的选择器参数试图帮助您了解和理解父集合和子集合之间的关系。代码更简洁,关系密切。

结果选择器是查询范围内可用的中间对象,为您提供所需的信息,由您自行决定在结果选择器中需要哪些数据来帮助您。

假设您需要一个结果集合,该集合不仅具有与所有联赛中的某些条件匹配的完整球队列表,而且需要知道球队所处的联赛。

例如:

var teamsAndTheirLeagues = from helper in leagues
    .SelectMany( l => l.Teams, ( league, team ) 
    => new { league, team } )
    where helper.team.Players.Count > 2 
        && helper.league.Teams.Count < 10
    select new { LeagueID = helper.league.ID, Team = helper.team };

这种语法为你提供了在where子句中查询团队和联盟的额外能力,而你却不会失去&#34;父子关系的后向连接。

可以找到有关selectMany投影的更详细说明herehere

答案 1 :(得分:0)

如果他们使用如下的示例,会更有帮助。了解我如何在Where中添加SelectMany子句。此重载使您可以灵活地运行查询,然后对该查询的结果进行投影:

// Project the pet owner's name and the pet's name.
var query =
    petOwners
    .SelectMany(petOwner => petOwner.Pets.Where(x => x.StartsWith("S")), (petOwner, petName) => new { petOwner, petName })
    //.Where(ownerAndPet => ownerAndPet.petName.StartsWith("S"))
    .Select(ownerAndPet =>
            new
            {
                Owner = ownerAndPet.petOwner.Name,
                Pet = ownerAndPet.petName
            }
    );