这个带有可变引用参数的 JavaScript 函数是纯函数吗?

如何解决这个带有可变引用参数的 JavaScript 函数是纯函数吗?

我和 this one 有同样的问题,但在 JavaScript 上下文中。

来自Wikipedia

[纯函数的] 返回值对于 same 参数是相同的

那里进一步声称纯函数不允许具有“可变引用参数”的返回值变化。在 JavaScript 中,每个普通对象都作为“可变引用参数”传递。考虑以下示例:

const f = (arr) => arr.length

const x = []
console.log( f(x) ) // 0
x.push(1);
console.log( f(x) ) // 1

以上证明 f 是不纯的吗? 或者您会争辩说我们在这两种情况下不会使用“相同”参数调用 f

我可以看到在其他线程可能会在 f 执行时混淆可变引用参数的语言/环境中调用 f 是多么有意义。但由于 f 不是 async,因此不可能发生这种情况。从调用 x 到执行完毕,f 将保持不变。 (如果我理解正确的话,这种解释似乎得到了 Verifiable Functional Purity in Java 的 § 4.1 中对“相同”的定义的支持。)

还是我遗漏了什么?在 JavaScript 中是否有一个例子,其中一个不包含异步代码的函数失去了 referential transparency 的属性,仅仅是因为它采用了可变引用,但如果我们使用例如改为 Immutable.js 数据结构?

解决方法

当用 Wikipedia definition 表示字母时,将可变数据结构(例如原生数组)的引用作为参数的函数不是纯函数:

对于相同的参数,它的返回值是相同的(没有变化局部静态变量、非局部变量、可变引用参数或来自 I/O 的输入流设备)。

等价

虽然这清楚地说“没有可变引用参数的变化”,但我们可以说这是可以解释的,取决于“相同”“变化”的含义。可能有不同的定义,因此我们进入意见领域。引自您提到的论文:

这些问题没有一个明显正确的答案。因此,确定性是一个参数化的属性:给定参数等效含义的定义,如果所有带有等效参数的调用返回的结果与语言内

在同一篇论文中提出的功能纯度,使用了以下等效定义:

如果两组对象引用产生相同的对象图,则认为它们是等效的

因此,根据该定义,以下两个数组被认为是等效的:

let a = [1];
let b = [1];

但是如果不添加更多限制,这个概念不能真正应用于 JavaScript。也不是 Java,这就是本文作者提到一种名为 Joe-E 的精简语言的原因:

对象有标识:概念上,它们有一个“地址”,我们可以使用==运算符比较两个对象引用是否指向同一个“地址”。这种对象身份的概念可能暴露出不确定性。

用 JavaScript 说明:

const compare = (array1,array2) => array1 === array2;

let arr = [1];
let a = compare(arr,arr);
let b = compare(arr,[1]);
console.log(a === b); // false

由于两个调用返回不同的结果,即使参数具有相同的形状和内容,我们也应该得出结论(根据这个等价的定义)上述函数 compare 不是纯函数。虽然在 Java 中您可以影响 == 运算符的行为(Joe-E 禁止调用 Object.hashCode),从而避免这种情况发生,但在比较对象时,这在 JavaScript 中通常是不可能的。

意想不到的副作用

另一个问题是 JavaScript 不是强类型的,因此函数无法确定它接收到的参数是否符合预期。例如,以下函数看起来很纯:

const add = (a,b) => a + b;

但它可以被调用以产生副作用:

const add = (a,b) => a + b;

let i = 0;
let obj = { valueOf() { return i++ } };
let a = add(1,obj);
let b = add(1,obj);
console.log(a === b); // false

您问题中的函数存在同样的问题:

const f = (arr) => arr.length;

const x = { get length() { return Math.random() } };
let a = f(x);
let b = f(x);
console.log(a === b) // false

在这两种情况下,函数都无意中调用了一个不纯函数并返回了一个依赖于它的结果。虽然在第一个示例中,仍然可以通过 typeof 检查轻松地使函数成为纯函数,但这对您的函数来说不是那么简单。我们可以想到 instanceofArray.isArray,甚至一些智能的 deepCompare 函数,但是,调用者仍然可以设置一个奇怪对象的原型,设置它的构造函数属性,用 getter 替换原始属性,包装代理中的对象,......等等,即使是最聪明的平等检查器也能愚弄。

务实

因为在 JavaScript 中有太多的“松散的结局”,一个人必须务实才能对“纯”有一个有用的定义,否则几乎没有什么可以被标记为纯的。

例如,实际上很多人会调用像 Array#slice 这样的函数 pure,即使它存在上述问题(包括与特殊参数 this 相关的问题)。

结论

在 JavaScript 中,当调用纯函数时,您通常必须就函数的调用方式达成一致。参数应该是某种类型,并且没有可以调用但不纯的(隐藏)方法。

有人可能会争辩说,这与“纯”背后的想法背道而驰,后者应该由函数定义本身决定,而不是最终可能被调用的方式。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)> insert overwrite table dwd_trade_cart_add_inc > select data.id, > data.user_id, > data.course_id, > date_format(
错误1 hive (edu)> insert into huanhuan values(1,'haoge'); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive> show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 <configuration> <property> <name>yarn.nodemanager.res