博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
引爆你的集合灵感 [C#, LINQ]
阅读量:6832 次
发布时间:2019-06-26

本文共 4725 字,大约阅读时间需要 15 分钟。

引爆你的集合灵感 [C#, LINQ]

SET FORTH YOUR SET IDEARS [C#, LINQ]

 

WRITTEN BY ALLEN LEE

 

0. TABLE OF CONTENT

  • 1. WHAT ARE THE DIFFERENCES?
  • 2. USING SET<T> COLLECTION OF POWERCOLLECTION.
  • 3. USING SET OPERATORS OF LINQ.
  • 4. SET<T> COLLECTION VS. SET OPERATORS.
  • 5. FURTHER CONSIDERATIONS.
  • 6. EXERCISES.

 

1. WHAT ARE THE DIFFERENCES?

某天,我的朋友 Heng 问我有什么工具可以比较两个文件夹,并找出其中不同的文件。首先,我们来看看他的要求:

  • 1) 仅需比较当前文件夹中的文件;
  • 2) 两个文件夹中相同名字的文件看作相同的文件。

很明显,如果把这两个文件夹可以看作两个集合,

  • A = { a | 第一个文件夹中的文件 }
  • B = { b | 第二个文件夹中的文件 }

那么他所找的文件将是对这两个集合进行一系列的集合操作所得的结果集。请看下图:

Venn diagram

红、黄两个圆分别代表 A 和 B 两个集合,其中1、2两个区域就是我们要找的“不同的文件”的集合了。使用集合表示法,这个结果集可以表示为(“'”为补集符号,全集 I = A ∪ B):

R = (A ∩ B)'

然而,这个结果集有一个盲点,就是不加区分的把1、2两个区域混合起来了,如果我们只需要其中一个区域的结果集呢?或者,我们需要把1、2两个区域分别显示到 GUI 的两个编辑框呢?

如果我们要以 A 作为基准,找出 B 中 A 没有的元素,那么结果集(区域2)将表示为:

R2 = A'

如果我们要以 B 作为基准,找出 A 中 B 没有的元素,那么结果集(区域1)将表示为:

R1 = B'

很明显,这一“方向”因素在具体编码的时候是应该加以考虑的。

搞了那么多理论,应该来点实际的了,首先,我们来看看 Set Collection 是如何使用的。

 

2. USING SET<T> COLLECTION OF POWERCOLLECTION.

首先,我们要获取指定路径下的所有文件,由于 Heng 只要求当前文件夹里的所有文件,于是:

None.gif
//
 Code #01
None.gif
None.gif
static
 
string
[] GetFileNamesFrom(
string
 path)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif
{
InBlock.gif    
if (path == null || path.Length == 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
throw new ArgumentException("Parameter cannot be null or empty!");
ExpandedSubBlockEnd.gif    }
InBlock.gif
InBlock.gif    
if (!Directory.Exists(path))
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
throw new ArgumentException(String.Format("{0} does not exist!", path));
ExpandedSubBlockEnd.gif    }
InBlock.gif
InBlock.gif    
string[] fileNames = System.IO.Directory.GetFiles(path);
InBlock.gif    
string[] result = new string[fileNames.Length];
InBlock.gif    
for (int i = 0; i < fileNames.Length; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        result[i] 
= System.IO.Path.GetFileName(fileNames[i]);
ExpandedSubBlockEnd.gif    }
InBlock.gif
InBlock.gif    
return result;
ExpandedBlockEnd.gif}

从 Heng 的要求中,我们可以看出只需要考虑文件名,所以我们把路径过滤掉了。

补充阅读:

,Allen Lee

接着,我们创建 A 和 B 两个集合:

None.gif
//
 Code #02
None.gif
None.gifSet
<
string
>
 A 
=
 
new
 Set
<
string
>
(GetFileNamesFrom(args[
0
]));
None.gifSet
<
string
>
 B 
=
 
new
 Set
<
string
>
(GetFileNamesFrom(args[
1
]));

对应上面的集合讨论,我们可以这样获取结果集:

None.gif
//
 Code #03
None.gif
None.gifSet
<
string
>
 R1 
=
 A.Difference(B);
None.gifSet
<
string
>
 R2 
=
 B.Difference(A);

我们知道 R 是 R1 和 R2 的并集,如果你需要 R,你不需要先找出 R1 和 R2 再进行合并,而是直接使用 Set 提供的:

None.gif
//
 Code #04
None.gif
None.gifSet
<
string
>
 R 
=
 A.SymmetricDifference(B);
None.gif
None.gif
//
 OR
None.gif
None.gifSet
<
string
>
 R 
=
 B.SymmetricDifference(A);

根据集合的性质,这两种做法是等效的。

现在,我们来看看 LINQ 中的 Set Operators 又是如何操作的。

 

3. USING SET OPERATORS OF LINQ.

有了 LINQ,你可以直接“查询”指定路径下的文件名(当然,你要保证路径的正确性):

None.gif
//
 Code #05
None.gif
None.gifvar A 
=
 from a 
in
 System.IO.Directory.GetFiles(args[
0
])
None.gif        orderby a 
None.gif        select System.IO.Path.GetFileName(a);
None.gifvar B 
=
 from b 
in
 System.IO.Directory.GetFiles(args[
1
])
None.gif        orderby b
None.gif        select System.IO.Path.GetFileName(b);

接着,使用 Standard Query Operators 的 Except 来获取结果集:

None.gif
//
 Code #06
None.gif
None.gifvar R1 
=
 A.Except(B);
None.gifvar R2 
=
 B.Except(A);

由于 LINQ 的集合操作符(Set operators)没有提供类似于 Set.SymmetricDifference 的功能,于是,如果你需要 R 的话,你可以:

None.gif
//
 Code #07
None.gif
None.gifvar R 
=
 R1.Union(R2);

另外,你可以对 System.IO.Directory.GetFiles 进行一番包装,在你的方法内检查传入的路径参数。

 

4. SET<T> COLLECTION VS. SET OPERATORS.

由于剧情发展的需要,它们俩难免会有这样一个碰面的情节。你会选择哪个?这样一个情节、这样一个问题,使人不禁感到像在选择一个将陪你度过下半辈子的另一半,呵呵~~~

就我个人而言,我喜欢 Set Collection 的成熟,但又忘不了 Set Operators with Query 的直率。贪心的我自然希望能够坐享齐人之福,集两家之长啦。

Martin Hotel 的 Sales & Marketing 主管 Becky 发现最近的酒店入住率下降了,她决定对此进行一番调查。

Becky 深知留住一个老客户的成本要比开发一个新客户的成本低得多,于是她决定看看最近一个季度的老客户入住率是否发生变化?如何变化?

None.gif
//
 Code #08
None.gif
None.gifvar q 
=
 from c 
in
 customers
None.gif        where c.Level 
==
 CustomerLevel.VIP
None.gif        group c by c.CheckInDate.Month into g
ExpandedBlockStart.gifContractedBlock.gif        select 
new
 
dot.gif
{ Month = g.Key, Count = g.Group.Count() }
;

Becky 发现以月为单位,老客户的入住率明显下降了,是什么原因呢?由于这些老客户都是大客户,他们只会入住五星级酒店,而本地的五星级酒店除了 Martin Hotel,就是剩下那家死对头了。Becky 发现最近有一些人经常停留在本酒店门口,她怀疑对手派人来本酒店门口拉客,于是,她找人调查那些在本酒店门口跟这些人接触后没有入住本酒店的老客户,她得到一份名单,上面列出在本酒店门口“失踪”却入住了对头酒店的老客户名字。

她把那些流失的老客户名单和已入住的老客户的名单加总:

None.gif
//
 Code #09
None.gif
None.gif
//
 GetLostVipList() returns an IEnumerable
None.gif
var l 
=
 GetLostVipList();
None.gifvar g 
=
 from c 
in
 customers
None.gif        where c.Level 
==
 CustomerLevel.VIP
None.gif        select c.Name;
None.gifvar t 
=
 g.Union(l);
None.gif
None.gif
//
 GetVipList() returns a Set
None.gif
Set
<
Customer
>
 vip 
=
 GetVipList();
None.gifSet
<
Customer
>
 total 
=
 
new
 Set
<
T
>
(t);
None.gif
None.gifConsole.WriteLine(vip.IsEqualTo(total));

现在,Becky 终于知道老客户流失的原因了,她决定起诉对头酒店使用不正当竞争手段。

 

5. FURTHER CONSIDERATIONS.

我们知道, 目前还只是一个原型(prototype),而 已经 release 了。然而,你不必为 LINQ 仅提供4个集合操作而烦恼,由于 LINQ 支持 Extension Methods,你可以根据需要自行扩展集合操作:

None.gif
//
 Code #10
None.gif
None.gif
namespace
 Becky.Utils
ExpandedBlockStart.gifContractedBlock.gif
dot.gif
{
InBlock.gif    
public static bool IsEqualTo<T>(this IEnumerable<T> first, IEnumerable<T> second)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
// Add some code heredot.gif
ExpandedSubBlockEnd.gif
    }
ExpandedBlockEnd.gif}

另外,由于集合的操作必然涉及到元素的判等,于是我们有必要对于这些工具的判等方式有一个了解。Set Collection使用 System.Collection.Generic.IEqualityComparer,而 Set Operators 则使用 Equals 和 GetHashCode 两个方法。如果你放入集合中的是自定义对象,那么你就要考虑这些问题了。

补充阅读:

Object Equality and Identity in Chapter 6: Common Object Operations. Jeffrey Richter. Applied Microsoft .NET Framework Programming. Microsoft Press, 2002

最后,由于本文把重点放在集合的操作以及工具的使用上,其他一些在实际的项目中必须考虑的因素已酌情省略了。例如实际进行文件对比时,相同名字的文件的大小也是一个需要考虑的因素。这样,你首先需要定义何谓不同的文件,提取必须考虑的因素,然后抽象出一个文件的表示,最后把“查询”的结果投射(project)到该抽象中。

 

6. EXERCISES.

Heng 现在增加多一点要求,就是要考虑文件的大小,仅当文件名和文件大小相同才看作相同的文件。那么,你认为 Set Collection 和 Set Operators 两个方案应该如何修改才能满足新的需求呢?另外,如果 Heng 过几天又可能提出新的要求,你认为你应该如何设计才能更有弹性的满足这种需求的变化呢?

转载地址:http://oejkl.baihongyu.com/

你可能感兴趣的文章
HSRP 技术
查看>>
网络基础~linux路由与网关、路由命令
查看>>
强大的联想4U机架式服务器ThinkSystem SR950
查看>>
美国防部:美国×××防御系统存在诸多安全问题
查看>>
阿里云搭建lamp平台
查看>>
Reverse Integer之Java实现
查看>>
Linux的SSH服务初学
查看>>
不同于FTP的另一款文件传输工具
查看>>
MYSQL 逻辑架构
查看>>
第11课--11_04_Linux网络配置之四 ifconfig及ip命令详解
查看>>
Linux命令之grep/sed/awk等行转列
查看>>
3.1 账户管理
查看>>
MySQL 多张表合并成一张表
查看>>
朋友圈广告投放优势及广告投放案例分享
查看>>
vivo Z3的Usb调试模式在哪里,开启vivo Z3Usb调试模式的教程
查看>>
能够让你提升的九个 Python 小技巧
查看>>
css3 greyscale实现去色 css3实现图片或页面变为黑白效果
查看>>
默认路由的配置
查看>>
AJPFX辨析Java中运算符 ++ 和 += 的区别
查看>>
如何在CAD中提取图纸上标注的内容
查看>>