汇编中的功能代码? (纯函数)
Functional code in assembly? (pure functions)
我目前正在 TASM 16 位中研究国际象棋。
我最近了解了纯函数以及它们是宇宙中最酷的东西,所以我的问题是,
是否应该竭尽全力使我的功能纯净且自包含且没有副作用?
问题在于,这样做会使代码更复杂一些,并且需要将更多参数传递给每个函数。
你们认为我应该怎么做?
感谢每个答案 <3
============================================= ==============
奖金问题:
什么是函数式编程?
我的理解是,它都是关于编写没有副作用的独立纯函数。
这样代码就非常容易阅读和管理。
我错了吗?
Should go out of my way to make my functions pure and self contained with no side effects?
我会以一些间接的方式回答,但具体针对“装配”,希望它能有所帮助。
任何不改变参数和结果集之外的东西的函数都是纯函数。 (相当宽松的定义,但很好开始。)在汇编程序中编写纯函数存在 none 障碍,前提是纯动作的定义与函数调用约定和 运行 环境一致。
我的意思是:首先,想象一个函数
- 在 R0 和 R1 中获取两个参数
- 在 R2 中求和
这真的很纯粹,如果它执行唯一的操作 - 添加两个值。
但是,想象一下调用约定需要在堆栈上传递参数。对于 x86-32,这将编译成类似于
f:
movl 8(%esp), %eax
addl 4(%esp), %eax
ret
从某种意义上说,这是纯粹的行动,除了 returning 值外,它不会显式更改任何内容,但有一个例外:它的调用更改了内存(堆栈区域)中的 12 个字节:2 个参数和 return 指针。这是另一种意义上的纯函数的允许副作用。
但是[2],如果您更改它,它会将第一个参数添加到全局变量:
f:
movl 4(%esp), %eax
addl %eax, sum_a
addl 8(%esp), %eax
ret
这将不是传统意义上的纯粹:您添加了副作用。
但是[3],如果某些副作用被明确声明为不影响函数纯度——例如,这个添加到 sum_a 的实现是为了调试,不会改变程序的目标功能——函数可以再次被视为纯函数。
所以,“纯度”并不是一个绝对的概念。只有在宣布哪些现实世界影响正在放弃其纯度而哪些没有放弃时,它才真正有意义。通常,以下效果保持纯度:
- 执行时间。
- 执行系统实现(硬件)效果,如 RAM 访问、缓存填充(和先前缓存状态的耗尽)。
- 操作系统(及其他软件)效果,如任务切换,分页in/out。
- 应用程序调试和监控(目标结果不变)。
允许的副作用由您决定。你要记住的主要事情是
- 某些影响是设计不可避免的(如缓存更改)。
- 尽管如此,您还是应该尽量减少所有其他副作用,因为它们很容易忘记,因此会在您写作时造成混淆。有时需要数小时的调试才能捕捉到 Total Recall,因为几年前编写的函数的副作用很小。这通常与语言无关:汇编程序或 Java,甚至 LISP,您的函数有重要的副作用,或者它们没有。
Bonus Question: What is functional programming?
The way I understood it is that it's all about writing self-contained, pure functions that have no side effects.
总的来说,不是。但这在这里是题外话(我的意思是在这个话题和这个论坛上,你最好去 SE 一个,除非通常的教科书,维基百科和谷歌搜索是不够的。)我最好在你指定实现时将它描述为编程在作为函数的基本动作及其参数-结果关系的意义上,没有指定操作顺序。但是我不会坚持的。
我目前正在 TASM 16 位中研究国际象棋。
我最近了解了纯函数以及它们是宇宙中最酷的东西,所以我的问题是,
是否应该竭尽全力使我的功能纯净且自包含且没有副作用?
问题在于,这样做会使代码更复杂一些,并且需要将更多参数传递给每个函数。
你们认为我应该怎么做? 感谢每个答案 <3
============================================= ==============
奖金问题: 什么是函数式编程?
我的理解是,它都是关于编写没有副作用的独立纯函数。
这样代码就非常容易阅读和管理。 我错了吗?
Should go out of my way to make my functions pure and self contained with no side effects?
我会以一些间接的方式回答,但具体针对“装配”,希望它能有所帮助。
任何不改变参数和结果集之外的东西的函数都是纯函数。 (相当宽松的定义,但很好开始。)在汇编程序中编写纯函数存在 none 障碍,前提是纯动作的定义与函数调用约定和 运行 环境一致。
我的意思是:首先,想象一个函数
- 在 R0 和 R1 中获取两个参数
- 在 R2 中求和
这真的很纯粹,如果它执行唯一的操作 - 添加两个值。
但是,想象一下调用约定需要在堆栈上传递参数。对于 x86-32,这将编译成类似于
f:
movl 8(%esp), %eax
addl 4(%esp), %eax
ret
从某种意义上说,这是纯粹的行动,除了 returning 值外,它不会显式更改任何内容,但有一个例外:它的调用更改了内存(堆栈区域)中的 12 个字节:2 个参数和 return 指针。这是另一种意义上的纯函数的允许副作用。
但是[2],如果您更改它,它会将第一个参数添加到全局变量:
f:
movl 4(%esp), %eax
addl %eax, sum_a
addl 8(%esp), %eax
ret
这将不是传统意义上的纯粹:您添加了副作用。
但是[3],如果某些副作用被明确声明为不影响函数纯度——例如,这个添加到 sum_a 的实现是为了调试,不会改变程序的目标功能——函数可以再次被视为纯函数。
所以,“纯度”并不是一个绝对的概念。只有在宣布哪些现实世界影响正在放弃其纯度而哪些没有放弃时,它才真正有意义。通常,以下效果保持纯度:
- 执行时间。
- 执行系统实现(硬件)效果,如 RAM 访问、缓存填充(和先前缓存状态的耗尽)。
- 操作系统(及其他软件)效果,如任务切换,分页in/out。
- 应用程序调试和监控(目标结果不变)。
允许的副作用由您决定。你要记住的主要事情是
- 某些影响是设计不可避免的(如缓存更改)。
- 尽管如此,您还是应该尽量减少所有其他副作用,因为它们很容易忘记,因此会在您写作时造成混淆。有时需要数小时的调试才能捕捉到 Total Recall,因为几年前编写的函数的副作用很小。这通常与语言无关:汇编程序或 Java,甚至 LISP,您的函数有重要的副作用,或者它们没有。
Bonus Question: What is functional programming?
The way I understood it is that it's all about writing self-contained, pure functions that have no side effects.
总的来说,不是。但这在这里是题外话(我的意思是在这个话题和这个论坛上,你最好去 SE 一个,除非通常的教科书,维基百科和谷歌搜索是不够的。)我最好在你指定实现时将它描述为编程在作为函数的基本动作及其参数-结果关系的意义上,没有指定操作顺序。但是我不会坚持的。