是否应该竭尽全力使我的功能纯净且自给自足而没有副作用?
我将以某种间接的方式回答,但具体到“组装”,希望它有所帮助。
任何不改变参数和结果集之外的东西的函数都是纯函数。(相当宽松的定义,但很好开始。)在汇编程序中编写纯函数没有任何障碍,只要纯动作的定义与函数调用约定和运行环境一致。
我的意思是:首先,想象一个函数
- 在 R0 和 R1 中获取两个参数
- 在 R2 中求和
如果它执行唯一的操作 - 添加两个值,这真的很纯粹。
但是,想象一下调用约定需要在堆栈上传递参数。对于 x86-32,这将编译为类似
f:
movl 8(%esp), %eax
addl 4(%esp), %eax
ret
这确实是纯粹的动作,除了返回值之外,它不会显式更改任何内容,但有一个例外:它的调用更改了内存(堆栈区域)中的 12 个字节:2 个参数和返回指针。这是另一种意义上的纯函数的允许副作用。
但是[2],如果你改变它,它将第一个参数添加到全局变量中:
f:
movl 4(%esp), %eax
addl %eax, sum_a
addl 8(%esp), %eax
ret
这不会是传统意义上的纯粹:您添加了副作用。
但是[3],如果某些副作用被明确声明为不影响函数纯度 - 例如,添加到 sum_a 是为了调试而实现的,并且不会改变程序的目标功能 - 该函数可以再次被视为纯函数。
所以,“纯度”并不是一个绝对的概念。只有当宣布现实世界的影响正在丢弃其纯度而哪些不是时,它才真正有意义。通常,以下效果是保持纯度:
- 执行时间处理时间。
- 执行系统实现(硬件)效果,如 RAM 访问、缓存填充(以及先前缓存状态的耗尽)。
- 操作系统(和其他软件)效果,如任务切换,分页/分页。
- 应用程序调试和监控(目标结果不变)。
允许的副作用取决于您。你要记住的主要事情是
- 有些影响是设计无法避免的(如缓存更改)。
- 尽管如此,你还是应该尽量减少所有其他副作用,因为它们很容易忘记,所以在写作时会让你感到困惑。有时,它需要数小时的调试才能捕捉到您几年前编写的函数的副作用的微小事实。这通常与语言无关:汇编程序或 Java,甚至 LISP,您的函数具有重要的副作用,或者它们没有。
额外问题:什么是函数式编程?
我的理解是,这一切都是关于编写没有副作用的自包含的纯函数。
一般来说,事实并非如此。但这在这里是题外话(我的意思是在这个主题和这个论坛中,你最好去SE one,除非通常的教科书、维基百科和谷歌搜索是不够的。)当你在基本的意义上指定实现时,我最好将其描述为编程作为函数的动作及其参数-结果关系,没有指定操作顺序。但我不会坚持。