Apply和数组:三个技巧

本文介绍了JavaScript中apply方法的三种实用技巧:通过apply配合Math.max获取数组中的最大值;使用apply填补数组中的空缺;利用apply扁平化数组。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

apply方法

apply是所有函数都有的方法.它的签名如下:

func.apply(thisValue, [arg1, arg2, ...])

如果不考虑thisValue的影响,上面的调用等同于:

func(arg1, arg2, ...)

也就是说,apply允许我们将一个数组"解开"成为一个个的参数再传递给调用函数.让我们分别看看apply使用中的三个技巧.

技巧1: 将一个数组传递给一个不接受数组作为参数的函数

JavaScript中没有返回一个数组中最大值的函数.但是,有一个函数Math.max可以返回任意多个数值类型的参数中的最大值.再配合apply,我们可以实现我们的目的:

> Math.max.apply(null, [10, -1, 5])
10

译者注:注意Math.max方法的参数中只要有一个值被转为NaN,则该方法直接返回NaN

>Math.max(1,null)  //相当于Math.max(1,0) 1
>Math.max(1,undefinded)  //相当于Math.max(1,NaN) NaN

>Math.max(0,-0)   //正零比负零大,和==不同 0
>Math.max(-0,-1)  //负零比-1大 -0

 技巧2: 填补稀疏数组

 数组中的缝隙

这里提醒一下读者:在JavaScript中,一个数组就是一个数字到值的映射.所以如果某个索引处缺失了一个元素(一条缝隙)和某个元素的值为undefined,是两种不同的情况.前者在被Array.prototype中的相关方法(forEach, map, 等.)遍历时,会跳过那些缺失的元素,而后者不会:

复制代码
> ["a",,"b"].forEach(function (x) { console.log(x) })
a
b

> ["a",undefined,"b"].forEach(function (x) { console.log(x) })
a
undefined
b
复制代码

译者注:这里作者说"数组就是一个数字到值的映射",严格意义上是不对的,正确的说法是"数组就是一个字符串到值的映射".下面是证据:

复制代码
>for (i in ["a", "b"]) {
    console.log(typeof i)  //数组的索引实际上是个字符串
 }
"string"
"string"

>["a", "b"].forEach(function (x, i) {
     console.log(typeof i) //这里的i实际上不是索引,只是个数字类型的累加器
 })
"number"
"number"
复制代码

你可以使用in运算符来检测数组中是否有缝隙.

> 1 in ["a",,"b"]false > 1 in ["a", undefined, "b"]true

译者注:这里之所以用1可以,是因为in运算符会把1转换成"1".

你过你尝试读取这个缝隙的值,会返回undefined,和实际的undefined元素是一样.

> ["a",,"b"][1]
undefined
> ["a", undefined, "b"][1]
undefined

译者注:[1]也会被转换成["1"]

填补缝隙

apply配合Array(这里不需要加new)使用,可以将数组中的缝隙填补为undefined元素:

> Array.apply(null, ["a",,"b"])
[ 'a', undefined, 'b' ]

这都是因为apply不会忽略数组中的缝隙,会把缝隙作为undefined参数传递给函数:

> function returnArgs() { return [].slice.call(arguments) }
> returnArgs.apply(null, ["a",,"b"])
[ 'a', undefined, 'b' ]

但需要注意的是,如果Array方法接收到的参数是一个单独的数字,则会把这个参数当成数组长度,返回一个新数组:

> Array.apply(null, [ 3 ])
[ , ,  ]

因此,最靠谱的方法是写一个这样的函数来做这种工作:

复制代码
function fillHoles(arr) {
    var result = [];
    for(var i=0; i < arr.length; i++) {
        result[i] = arr[i];
    }
    return result;
}
复制代码

执行:

> fillHoles(["a",,"b"])
[ 'a', undefined, 'b' ]

Underscore中的_.compact函数会移除数组中的所有假值,包括缝隙:

复制代码
> _.compact(["a",,"b"])
[ 'a', 'b' ]
> _.compact(["a", undefined, "b"])
[ 'a', 'b' ]
> _.compact(["a", false, "b"])
[ 'a', 'b' ]
复制代码

技巧3: 扁平化数组

任务:将一个包含多个数组元素的数组转换为一个一阶数组.我们利用apply解包数组的能力配合concat来做这件事:

> Array.prototype.concat.apply([], [["a"], ["b"]])
[ 'a', 'b' ]

混合非数组类型的元素也可以:

> Array.prototype.concat.apply([], [["a"], "b"])
[ 'a', 'b' ]

apply方法的thisValue必须指定为[],因为concat是一个数组的方法,不是一个独立的函数.这种写法的限制是最多只能扁平化二阶数组:

> Array.prototype.concat.apply([], [[["a"]], ["b"]])
[ [ 'a' ], 'b' ]

所以你应该考虑一个替代方案.比如Underscore中的_.flatten函数就可以处理任意层数的嵌套数组:

> _.flatten([[["a"]], ["b"]])
[ 'a', 'b' ]


http://www.2ality.com/2012/07/apply-tricks.html

本文转自艾伦 Aaron博客园博客,原文链接:http://www.cnblogs.com/aaronjs/archive/2013/05/26/3099969.html,如需转载请自行联系原作者
<think>我们参考用户上一次的问题回答历史,用户希望了解如何使用mpmath库实现高精度的加减乘除运算,并将结果保存到数组。根据引用[1],mpmath支持有理数的高精度操作,包括加减乘除等基本运算。引用[2]提到mpmath可以与numpy互操作,将高精度数值转换为numpy数组。因此,我们将分为以下几个步骤:1.设置mpmath的计算精度。2.创建高精度数值(使用字符串避免浮点误差)。3.进行高精度的加减乘除运算。4.将结果保存到数组(列表或numpy数组)。同时,我们注意到用户可能涉及数组存储,所以会展示如何将多个高精度数存储在列表中,以及如何与numpy数组转换(但注意numpy数组本身不支持任意高精度,转换到numpy数组会转为普通浮点数,所以如果需要保持高精度,应使用列表存储mpmath类型)。具体步骤:</think>### 如何使用 mpmath 实现高精度运算并保存到数组 以下是基于引用[1][2]的实现指南,重点解决高精度计算数组存储问题: #### 1. 基础高精度计算 ```python import mpmath # 设置精度为500位小数 mpmath.mp.dps = 500 # 创建高精度数字(使用字符串避免浮点误差) a = mpmath.mpf('0.12345678901234567890') b = mpmath.mpf('9876543210.1234567890') # 高精度四则运算 add_result = a + b # 加法 $a+b$ sub_result = a - b # 减法 $a-b$ mul_result = a * b # 乘法 $a \times b$ div_result = a / b # 除法 $\frac{a}{b}$ print(f"乘法结果: {mul_result}") # 显示500位精度结果 ``` #### 2. 数组存储与操作 ```python # 创建高精度数组 precision_array = mpmath.matrix([ mpmath.mpf('1.234567890123456789'), mpmath.mpf('0.000000000000000001'), mpmath.mpf('3.141592653589793238') ]) # 数组运算 squared_array = precision_array.apply(lambda x: x**2) # 平方运算 sum_array = mpmath.fsum(precision_array) # 高精度求 # 保存到文件 with open('high_precision_data.txt', 'w') as f: for num in precision_array: f.write(mpmath.nstr(num, 50) + '\n') # 保存50位有效数字 ``` #### 3. 与 NumPy 协作(引用[2]) ```python import numpy as np # 转换为NumPy数组(会损失精度) numpy_array = np.array([float(x) for x in precision_array]) # 从NumPy导入高精度数据 numpy_data = np.array([0.1, 0.2, 0.3]) mpmath_array = mpmath.matrix([mpmath.mpf(str(x)) for x in numpy_data]) ``` #### 4. 复杂运算示例 ```python # 创建高精度常数数组 constants = mpmath.matrix([ mpmath.pi, # π mpmath.exp(1), # e (1 + mpmath.sqrt(5))/2 # 黄金分割率 $\phi$ ]) # 矩阵运算 matrix_a = mpmath.matrix([ [mpmath.mpf('1.23e-50'), mpmath.mpf('2.34e-100')], [mpmath.mpf('5.67e-150'), mpmath.mpf('9.87e-200')] ]) inverse_matrix = matrix_a**-1 # 高精度矩阵求逆 ``` #### 5. 性能优化技巧 1. **批量处理**:使用`mpmath.matrix`代替列表 ```python # 高效创建1000个高精度数 large_array = mpmath.matrix([mpmath.mpf(f'0.{i}'*50) for i in range(1000)]) ``` 2. **内存管理**:及时释放大对象 ```python del large_array # 显式释放内存 mpmath.mp.prec = 100 # 临时降低精度计算 ``` 3. **并行计算**(需第三方库支持): ```python from multiprocessing import Pool def compute_sin(x): return mpmath.sin(x) with Pool() as p: results = p.map(compute_sin, precision_array) ``` ### 关键特性对比 | 操作类型 | 普通浮点数 | mpmath高精度 | |---------|-----------|-------------| | 精度上限 | 15-17位 | 任意位数(通过`dps`设置) | | 数组存储 | `numpy.array` | `mpmath.matrix` | | 极小值处理 | 下溢为0 | 精确表示(如$10^{-1000}$) | | 大数运算 | 溢出错误 | 精确计算(如$10^{1000!}$)[^1] | ### 典型应用场景 1. **金融计算**:复利公式的精确计算 ```python principal = mpmath.mpf('1000000000.00') # 10亿元本金 rate = mpmath.mpf('0.000000001') # 极低利率 result = principal * (1 + rate)**365 # 精确计算 ``` 2. **科学计算**:避免舍入误差累积 ```python # 级数求 S = Σ(1/n²) from 1 to ∞ = π²/6 n = mpmath.mpf('1000000') series_sum = mpmath.nsum(lambda k: 1/k**2, [1, mpmath.inf]) ``` 3. **密码学**:大整数运算 ```python prime1 = mpmath.mpf('2**82589933-1') # 已知最大素数 prime2 = mpmath.mpf('2**77232917-1') product = prime1 * prime2 # 精确计算超大乘积 ``` > **重要提示**:当处理$10^{-50}$级别的极小值或$10^{1000}$级别的极大值时,务必使用字符串初始化(`mpmath.mpf('1e-1000')`),直接使用浮点数(`1e-1000`)会立即归零[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值