引言
我最近正在学习面向数据编程,在此过程中逐渐接触到了函数式编程,并被其理论思想所折服,希望能够和大家分享下我的学习心得。 下面将结合一个实际工程问题,来讨论下函数式编程其中之一的特性:Immutable,以及这种原则是如何来指导我们的实际工作。
实际工程介绍
如图所示,为一个3*3的九宫格,蓝色格子代表其中存放有数据,白色格子代表是个空格子。我的目标是:找到格子(x,y)上下左右四方向中所有的空格子,并将空格坐标以List<VectorInt2>的形式输出。 我的开发环境是Unity2020.1.14。
#错误示范 我觉得代码非常简单,只要
遍历
四个方向的格子就好了,如果格子中有数据就remove掉就好了。据此,很快就写出了代码:
//将四方邻近的格子坐标装入List
var
adjacentGrids
=
new
List
<
Vector2Int
>
(
)
;
adjacentGrids
.
Add
(
gridPos
+
Vector2Int
.
left
)
;
adjacentGrids
.
Add
(
gridPos
+
Vector2Int
.
right
)
;
adjacentGrids
.
Add
(
gridPos
+
Vector2Int
.
up
)
;
adjacentGrids
.
Add
(
gridPos
+
Vector2Int
.
down
)
;
//遍历List
foreach
(
var
item
in
adjacentGrids
)
{
if
(
HasData
(
item
)
)
//如果格子中有数据,就从adjacentGrids中remove
{
adjacentGrids
.
Remove
(
item
)
;
}
}
return
adjacentGrids
;
但是测试结果出人意料:adjacentGrids.Count期望值为1,实际运行结果为2或者3。 为什么会这样呢?很奇怪啊!逻辑看上去没有问题,编辑器也没有报错,还出现了两个结果。
#为什么错了 让我们来一步步检查下迭代过程。 第一步,检查左侧格子,没有数据,下一步。 第二步,检查右侧格子,有数据,移除,下一步。 第三步就会出现非常奇怪的事情:第二步中对 adjacentGrids做出了修改,这时编辑器可能会直接终止迭代,可能继续迭代,但会抛出异常。 这就导致了运行结果很奇怪!
#函数式编程是怎样避免这种错误的 函数式编程告诉我们不要直接去操作adjacentGrids,而是要去拷贝adjacentGrids。以下是修改代码:
List
<
Vector2Int
>
temp
=
new
List
<
Vector2Int
>
(
)
;
//temp作为adjacentGrids
for
(
int
i
=
0
;
i
<
4
;
i
++
)
{
if
(
!
HasData
(
adjacentGrids
[
i
]
)
)
//如果格子中无数据,将格子坐标压入temp
{
temp
.
Add
(
adjacentGrids
[
i
]
)
;
}
}
return
temp
;
但是函数式编程在哪里呢?这些不都是命令式的吗?
对于这些问题,我觉得都无关紧要。我认为明白什么时候会出问题?为什么会出问题?从这个问题中我们能够得到什么?这些理论知识才是重要的。我认为所谓理论是前人经验教训的总结,这些总结比我们的个人教训要更加宝贵。对于函数式编程,我们不要局限于用工具的层面上。要明白什么时候用什么工具才行。
在这个例子中,函数式编程这样解决:
return
adjacentGrids
.
Where
(
x
=>
!
HasData
(
x
)
)
.
ToList
(
)
;
我用的插件是UniRx,当然使用Rx.Net也可以达到相同的效果。对于具体的语法特性如Where代表什么,可能有读者会感到疑惑。操作符Where的具体工作情况见:
http://reactivex.io/
但是无论使用哪种工具都是基于同一种理论,采用同样的操作符名称。很多人抱怨函数式编程很难理解,主要原因在于缺乏一些基础理论的支持。
比如代码中的Where其实就是LinQ中的Where操作符,至于为什么,为此我强烈推荐大家看下这本书《Rx.Net in Action》[1]。这本书很详细的介绍了面向数据、函数式的理论,对于大家的工作会有极大助力。
在之后的文章中,我会结合各种问题对知识点做出介绍。
#参考书目 [1] Tamir Dresher. Rx.Net in Action.
https://www.manning.com/books/rx-dot-net-in-action
本文探讨了在学习函数式编程中遇到的问题,通过Unity实现的九宫格案例,展示了Immutable原则如何避免数据操作中的意外。作者解释了错误原因,并对比了命令式和函数式编程思路,提倡理解理论背景以有效应用工具。

被折叠的 条评论
为什么被折叠?



