iT邦幫忙

2022 iThome 鐵人賽

DAY 20
0

再看一次昨天的程式碼的片段

GetMemberByNameAsync(name)
	.TaskBind(member => member
					.Map(m => new Attendance{ name = m.Name })
					.Map(CreateAttendanceAsync))

這段程式碼看起來有礙觀瞻,在TaskBind裡面塞了一大坨東西可讀性說不上很好,那有沒有辦法把裡面的Map拉到外面來跟TaskBind對齊,可能長的像這樣呢?

GetMemberByNameAsync(name)
.TaskBind(...)
.Map(...)

先離個題,我們來看看Linq裡面的SelectMany

SelectMany的多載

通常我們使用SelectMany是在攤平兩層集合時,大概是下面的作法

public static IEnumerable<TResult> SelectMany<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, IEnumerable<TResult>> selector) 
{
  foreach (var item in source) 
  {
    foreach (var subItem in selector(item)) 
    {
      yield return subItem;
    }
  }
}

首先遍歷最外層的集合,將我們需要的子集合挑出(selector做的事情),然後再遍歷子集合一次,用yield return把子集合裡面的元素一一傳出來,這邊要注意foreach是一個把物件從IEnumerable這個盒子中拿出來的方法,明天再次講到Task物件時會用到類似的概念。

SelectMany還有另外一個多載

public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, IEnumerable<TCollection>> collectionSelector,
    Func<TSource, TCollection, TResult> resultSelector) 
{
  foreach (var item in source)
  {
    foreach (var subItem in collectionSelector(item)) 
    {
      yield return resultSelector(item,subItem);
    }
  }
}

除了單純的拋出元素,其實我也可以對要拋出來的元素做加工(resultSelector),而這個功能有什麼用呢?

var list1 = new List<string> { "洛伊德", "約兒", "安妮亞" };
var list2 = new List<string> { "佛傑" };

list1
    .SelectMany(
				x => list2,
        (a, b) => $"{a}.{b}")
    .ToList()
    .ForEach(Console.WriteLine);
// 洛伊德.佛傑
// 約兒.佛傑
// 安妮亞.佛傑

//Query expression
from a in list1 
from b in list2
select $"{a}.{b}"

在這個多載中可以看到兩個集合被組合在一起,如果用查詢運算式的話是不是就達到了把SelectMany裡面的東西提出來拉平的效果呢?

小結

今天介紹了可以透過SelectMany的多載來達到拉平的效果,基於函數可以作為委派傳遞,實物上list1與list2的內容也可以放待執行的函數,而且SelectMany是可以無限串連的!只要把Map/Bind寫成Select/SelectMany,欺騙C#,任何的單子都可以用查詢運算式組合。明天就來幫Task還有Nullable重寫SelectMany看看效果吧!當初看到這個真的覺得是什麼巫術,實在太神奇了!!!!!


上一篇
Day19. Task(2)
下一篇
Day21. SelectMany(2)
系列文
Functional Programming with C#30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言