提问人:David542 提问时间:8/9/2023 最后编辑:David542 更新时间:8/18/2023 访问量:264
解包/打包操作员
Unpack / pack operator
问:
我正在寻找实现打包/拆包操作员的各种方法。举个例子:
*[1,2,3] --> 1,2,3 (one array scalar value unpacked to three values)
*1,2,3 --> [1,2,3] (three values packed to one scalar array value)
这是语言中的常见运算符吗?如果是这样,它通常如何表示?
其他可能的术语:
- 解构/结构化
- 开箱/打包
答:
实现此目的的方法取决于语言。
在 python 中,可以使用元组打包/解包值:
mytuple = (1,2,3) # Packing into a tuple
(a,b,c) = mytuple # Unpacking
print(f'{a}, {b}, {c}')
在 c++11 中,可以使用以下方法完成:std::tie()
#include <iostream>
#include <tuple>
int main()
{
auto mytuple = std::tuple<int,int,int>{1,2,3}; // Packing into a tuple
int a, b, c;
std::tie(a,b,c) = mytuple; // Unpacking (std::tie)
std::cout << a << " " << b << " " << c << std::endl;
return 0;
}
而且,在 c++17 及更高版本中,可以使用结构化绑定:
#include <iostream>
#include <tuple>
int main()
{
auto mytuple = std::tuple{1,2,3}; // Packing into a tuple
auto[a,b,c] = mytuple; // Unpacking (structured binding)
std::cout << a << " " << b << " " << c << std::endl;
return 0;
}
打包和解包,也称为解构和结构化,是许多编程语言中的常见概念。它允许在单个语句中将多个值分配给多个变量,或者将多个值作为参数传递给函数。
在 C++ 中,可以使用结构化绑定(从 C++17 开始)将数组或元组的元素解压缩为单独的变量:
#include <array>
#include <iostream>
#include <tuple>
int main() {
std::array<int, 3> myarray = {1, 2, 3};
auto [a, b, c] = myarray;
std::cout << a << ' ' << b << ' ' << c << '\n'; // prints 1 2 3
std::tuple<int, int, int> mytuple = {4, 5, 6};
auto [x, y, z] = mytuple;
std::cout << x << ' ' << y << ' ' << z << '\n'; // prints 4 5 6
}
在 JavaScript 中,展开运算符 (...) 用于解包。它的工作方式类似于 Python 中的星号运算符:
function myfunc(a, b, c) {
console.log(a, b, c);
}
let myarray = [1, 2, 3];
myfunc(...myarray); // prints 1 2 3
在 Python 中,星号 (*) 运算符用于解包。例如,您可以使用它来解压缩列表或元组的元素,并将它们作为单独的参数传递给函数:
def myfunc(a, b, c):
print(a, b, c)
mylist = [1, 2, 3]
myfunc(*mylist) # prints 1 2 3
语法和行为可能因语言及其版本而异
我将描述Python中的整个相关工作和语法。
将变量打包为集合,并将可迭代文件解压缩为某些变量:
iterable = (1, 2, 3, 4) # Packing by Tuple explicitly
iterable = 1, 2, 3, 4 # Packing by Tuple implicitly
iterable = [1, 2, 3, 4] # Packing by List
nested_iterable = (1, 2, (4, 5, 6)) # Packing nested items
mapping = {"a": 1, "b": 2}
a, b, c, d = iterable # Unpacking using Tuple implicitly
(a, b, c, d) = iterable # Unpacking using Tuple explicitly
[a, b, c, d] = iterable # Unpacking using List
a, _, _, d = iterable # Ignoring some values by conventionally using the _ (called Throwaway) variable
a, *middle_items, d = iterable # Using * to group remaining items as a List
a, b, c, *d = iterable # Using * for a single element is also grouped as a single item List
a, b, c, d, *e = iterable # If there are no other elements, e becomes an empty List
a, *_ = iterable # Ignoring remaining items
a, b, (c, d, e) = nested_iterable # Unpacking nested items using Tuple
a, b, [c, d, e] = nested_iterable # Unpacking nested items using mixed Tuple and List
a_key, b_key = mapping # The items get iterated for unpacking, so, here, only keys got returned
(a_key, a_value), (b_key, b_value) = mapping.items() # Unpacking a mapping using items method
注意:不能在一次解包中多次应用运算符。*
将可迭代对象解压缩为位置参数,将映射解压缩为命名参数,以及打包剩余的位置和命名参数:
def normal_function(a, b, c, d):
...
items = [2, 3, 4, 5]
mapping = {"a": 2, "b": 3, "c": 4, "d": 5}
normal_function(*items) # Positional unpacking to function arguments.
normal_function(**mapping) # Named unpacking to function arguments.
def variadic_function(a, b, c, *remaining_positional_args, d, e, f, **remaining_named_args):
# remaining_positional_args is a Tuple and remaining_named_args is a Dict
# d, e, f must be filled by the name
...
以下是数组解构和结构化在不同语言中的实现方式:
JS的
解构:
const [a, b, c] = [1, 2, 3]; // Unpacks the array into individual variables a, b, and c
构建:
const packedArray = [a, b, c]; // Packs the values into a new array
皮
解构:
a, b, c = (1, 2, 3) # Unpacks the tuple into individual variables a, b, and c
构建:
packed_tuple = (a, b, c) # Packs the values into a new tuple
CPP系列
C++ 没有像 JavaScript 或 Python 那样用于数组解构或结构化的直接内置机制。但是,您可以使用以下方法获得类似的结果:
解构: 您可以手动将数组中的值分配给各个变量。
int arr[] = {1, 2, 3};
int a = arr[0];
int b = arr[1];
int c = arr[2];
构建: 您可以创建数组或数据结构来保存这些值。
int a = 1;
int b = 2;
int c = 3;
int arr[] = {a, b, c};
你提到了数组,但打包和解包只是分别意味着,在将整个包传递给函数之前,从多个实体中产生 1 个边界,并将包的组成部分作为单独的参数传递给函数。
实际的捆绑包(数组、元组、结构)是一种实现细节,只要你有办法从成分中创建一个,并且有办法从成分中拉出。
在 C++ 中,a 是首选,因为这样可以自由选择可以捆绑在一起的实体的数量和类型。A 会限制你对所有实体使用相同的类型,所以在大多数场景中都是不可能的。std::tuple
std::array
因此,当您想在将更多实体传递给函数之前将它们打包在一起时,只需使用 .std::make_tuple
对于解包,情况更加复杂,但幸运的是有一些现成的库解决方案:Boost.Hana 提供 boost::hana::unpack
,它允许你做这样的事情
constexpr auto add = [](auto x, auto y, auto z) {
return x + y + z;
};
std::cout << hana::unpack(hana::make_tuple(1, 2, 3), add); // prints 6
(该示例使用 boost::hana::tuple
,但扩展 Hana 也很容易为 s 工作。hana::unpack
std::tuple
在这方面,结构化绑定是一种最小的构建块,因为它们允许您在将单个实体传递给所需函数之前为它们命名。参考上面的例子,你可以把它改写成std::tie
constexpr auto add = [](auto x, auto y, auto z) {
return x + y + z;
};
auto const& [a, b, c] = std::make_tuple(1, 2, 3);
std::cout << add(a, b, c); // prints 6
这有点麻烦,因为你必须发明一个实际上可能没有多大意义的名字,这取决于具体情况,很明显;很明显,如果元组只是不相关事物的集合,它们将采用不相关的代码路径,那么结构化绑定可能更有意义。
在其他语言中,打包和拆包更简单。以 JavaScript 为例,其中数组可以是以太源的;打包更多的东西只是意味着将它们包含在一个以 - 分隔的列表中。对于开箱,有操作员。下面是本机 JavaScript 代码,对应于上面的 C++ 代码,它使用了一个完整的库:,
[
]
...
add = x => x[0] + x[1] + x[2]
console.log(add([1, 2, 3])) // prints 6
add = (x, y, z) => x + y + z;
console.log(add(...[1, 2, 3])) // prints 6
最后,在像 Haskell 这样的语言中,函数都是 curried,即它们一个接一个地接受参数,打包和解包分别与 uncurrying 和 currying 基本一致:
add3'curried = \x y z -> x + y + z -- === curry3 add3'uncurried
triple = (1, 2, 3)
uncurry3 add3'curried $ triple -- prints 6
add3'uncurried = \(x, y, z) -> x + y + z -- === uncurry3 add3'curried
curry3 add3'uncurried 1 2 3 -- prints 6
评论