解包/打包运算符

huangapple go评论132阅读模式
英文:

Unpack / pack operator

问题

我正在寻找打包/解包操作符的不同实现方式。以一个例子来说明:

*[1,2,3]    --> 1,2,3     (将一个数组标量值解包为三个值)
*1,2,3      --> [1,2,3]   (将三个值打包为一个标量数组值)

这是一种常见的操作符吗?如果是的话,通常如何表示?

其他可能的术语:

  • 解构/构造
  • 解包/打包
英文:

I'm looking to find various ways that a packing / unpacking operator is implemented. As an example:

*[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)

Is this a common operator in languages? If so, how is it usually represented?

Other possible terms:

  • Destructuring / structuring
  • Unpacking / packing

答案1

得分: 3

实现这个的方式取决于编程语言。

在Python中,可以使用元组进行打包/解包:

mytuple = (1,2,3)   # 打包成元组
(a,b,c) = mytuple   # 解包
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}; // 打包成元组
    int a, b, c;
    std::tie(a,b,c) = mytuple;                     // 解包(使用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}; // 打包成元组
    auto[a,b,c] = mytuple;            // 解包(使用结构化绑定)
    std::cout << a << " " << b << " " << c << std::endl;
    return 0;
}
英文:

The way to achieve this depends on the language.

In python values can be packed / unpacked using tuples:

mytuple = (1,2,3)   # Packing into a tuple
(a,b,c) = mytuple   # Unpacking
print(f&#39;{a}, {b}, {c}&#39;)

In c++11, it can be done by using std::tie():

#include &lt;iostream&gt;
#include &lt;tuple&gt;

int main()
{
    auto mytuple = std::tuple&lt;int,int,int&gt;{1,2,3}; // Packing into a tuple
    int a, b, c;
    std::tie(a,b,c) = mytuple;                     // Unpacking (std::tie)
    std::cout &lt;&lt; a &lt;&lt; &quot; &quot; &lt;&lt; b &lt;&lt; &quot; &quot; &lt;&lt; c &lt;&lt; std::endl;
    return 0;
}

And, in c++17 and newer, structured bindings can be used:

#include &lt;iostream&gt;
#include &lt;tuple&gt;

int main()
{
    auto mytuple = std::tuple{1,2,3}; // Packing into a tuple
    auto[a,b,c] = mytuple;            // Unpacking (structured binding)
    std::cout &lt;&lt; a &lt;&lt; &quot; &quot; &lt;&lt; b &lt;&lt; &quot; &quot; &lt;&lt; c &lt;&lt; std::endl;
    return 0;
}

答案2

得分: 2

我将描述整个相关工作和Python中的语法。

将变量打包为集合并将可迭代对象解包为一些变量:

iterable = (1, 2, 3, 4)  # 通过元组显式打包
iterable = 1, 2, 3, 4  # 通过元组隐式打包
iterable = [1, 2, 3, 4]  # 通过列表打包
nested_iterable = (1, 2, (4, 5, 6))  # 打包嵌套项
mapping = {"a": 1, "b": 2}

a, b, c, d = iterable  # 使用元组隐式解包
(a, b, c, d) = iterable  # 使用元组显式解包
[a, b, c, d] = iterable  # 使用列表解包
a, _, _, d = iterable  # 通过惯例使用_(称为丢弃)变量忽略某些值
a, *middle_items, d = iterable  # 使用*将剩余项分组为列表
a, b, c, *d = iterable  # 使用*将单个元素分组为单个项列表
a, b, c, d, *e = iterable  # 如果没有其他元素,e将成为一个空列表
a, *_ = iterable  # 忽略剩余项
a, b, (c, d, e) = nested_iterable  # 使用元组解包嵌套项
a, b, [c, d, e] = nested_iterable  # 使用混合元组和列表解包嵌套项
a_key, b_key = mapping  # 项被迭代以解包,所以这里只返回了键
(a_key, a_value), (b_key, b_value) = mapping.items()  # 使用items方法解包映射

注意在单个解包中不能多次使用`*`运算符

将可迭代对象解包为位置参数和映射解包为命名参数同时打包剩余的位置和命名参数

```python
def normal_function(a, b, c, d):
    ...

items = [2, 3, 4, 5]
mapping = {"a": 2, "b": 3, "c": 4, "d": 5}

normal_function(*items)  # 将位置解包为函数参数。
normal_function(**mapping)  # 将命名解包为函数参数。

def variadic_function(a, b, c, *remaining_positional_args, d, e, f, **remaining_named_args):
    # remaining_positional_args是一个元组,remaining_named_args是一个字典
    # d、e、f必须通过名称填充
    ...
英文:

I will describe the whole related works and syntax in Python.

Packing variables as a collection and unpacking iterables to some variables:

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 = {&quot;a&quot;: 1, &quot;b&quot;: 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

Note: You cannot apply the * operator multiple times in a single unpacking.

Unpacking iterables as positional arguments and mappings as named arguments along with Packing remaining positional and named arguments:

def normal_function(a, b, c, d):
    ...

items = [2, 3, 4, 5]
mapping = {&quot;a&quot;: 2, &quot;b&quot;: 3, &quot;c&quot;: 4, &quot;d&quot;: 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
    ...

答案3

得分: 1

打包和解包,也被称为解构和构造,在许多编程语言中都是一个常见的概念。它允许在单个语句中将多个值分配给多个变量,或者将多个值作为参数传递给函数。

在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'; // 输出 1 2 3

     std::tuple<int, int, int> mytuple = {4, 5, 6};
     auto [x, y, z] = mytuple;
     std::cout << x << ' ' << y << ' ' << z << '\n'; // 输出 4 5 6
}

在JavaScript中,展开运算符(...)用于解包。它的工作方式类似于Python中的星号运算符:

function myfunc(a, b, c) {
    console.log(a, b, c);
}

let myarray = [1, 2, 3];
myfunc(...myarray); // 输出 1 2 3

在Python中,星号(*)运算符用于解包。例如,你可以使用它来解包列表或元组的元素,并将它们作为单独的参数传递给函数:

def myfunc(a, b, c):
    print(a, b, c)

mylist = [1, 2, 3]
myfunc(*mylist) # 输出 1 2 3

语法和行为可能因语言及其版本而异。

英文:

Packing and unpacking, also known as destructuring and structuring, is a common concept in many programming languages. It allows for the assignment of multiple values to multiple variables in a single statement, or for the passing of multiple values as arguments to a function.

In C++, you can use structured bindings (since C++17) to unpack the elements of an array or tuple into separate variables:

#include &lt;array&gt;
#include &lt;iostream&gt;
#include &lt;tuple&gt;

int main() {
     std::array&lt;int, 3&gt; myarray = {1, 2, 3};
     auto [a, b, c] = myarray;
     std::cout &lt;&lt; a &lt;&lt; &#39; &#39; &lt;&lt; b &lt;&lt; &#39; &#39; &lt;&lt; c &lt;&lt; &#39;\n&#39;; // prints 1 2 3

     std::tuple&lt;int, int, int&gt; mytuple = {4, 5, 6};
     auto [x, y, z] = mytuple;
     std::cout &lt;&lt; x &lt;&lt; &#39; &#39; &lt;&lt; y &lt;&lt; &#39; &#39; &lt;&lt; z &lt;&lt; &#39;\n&#39;; // prints 4 5 6
     }

In JavaScript, the spread operator (…) is used for unpacking. It works similarly to the asterisk operator in Python:

function myfunc(a, b, c) {
    console.log(a, b, c);
}

let myarray = [1, 2, 3];
myfunc(...myarray); // prints 1 2 3

In Python, the asterisk (*) operator is used for unpacking. For example, you can use it to unpack the elements of a list or tuple and pass them as separate arguments to a function:

def myfunc(a, b, c):
    print(a, b, c)

mylist = [1, 2, 3]
myfunc(*mylist) # prints 1 2 3

> The syntax and behavior may vary depending on the language and its version

答案4

得分: 1

你提到了数组,但是打包和解包只是指在将整个打包传递给函数之前,将多个实体组合成一个束,以及将束的组成部分作为单独的参数传递给函数。

束实际上是什么(数组、元组、结构体)在某种程度上是一种实现细节,只要你有一种方法将其组成部分创建为一个束,并且有一种方法从一个束中提取出组成部分。

在C++中,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); // 输出 6

(这个示例使用了 boost::hana::tuple,但很容易扩展 Hana 使 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); // 输出 6

这有点麻烦,因为你必须发明一个可能在具体情况下实际上没有太多意义的名称;同样清楚的是,如果元组只是一组将采用不相关代码路径的不相关事物,那么结构化绑定可能更有意义。


在其他语言中,打包和解包更直接。以 JavaScript 为例,数组可以是异构的;打包更多的东西只是将它们包含在 [] 中的逗号分隔列表中。对于解包,有 ... 运算符。下面是对应于上面使用完整库的 C++ 代码的本机 JavaScript 代码:

add = x => x[0] + x[1] + x[2];
console.log(add([1, 2, 3])); // 输出 6
add = (x, y, z) => x + y + z;
console.log(add(...[1, 2, 3])); // 输出 6

最后,在像 Haskell 这样的语言中,函数都是柯里化的,即它们逐个接受参数,打包和解包基本上与非柯里化和柯里化相对应:

add3'curried = \x y z -> x + y + z -- === curry3 add3'uncurried
triple = (1, 2, 3)
uncurry3 add3'curried $ triple -- 输出 6

add3'uncurried = \(x, y, z) -> x + y + z -- === uncurry3 add3'curried
curry3 add3'uncurried 1 2 3 -- 输出 6
英文:

You mention array, but packing and unpacking just mean, respectively, making 1 boundle out of multiple entities before passing the whole bundle to a function, and passing the constituents of a bundle as separate arguemnts to a function.

What the bundle actualy is (an array, a tuple, a struct) is kind of an implementation detail, as long as you have a way to create one out of the constituensts, and a way to pull the constituents out of one.

In C++, a std::tuple is preferrable, because that gives you freedom on both the number and the types of entities you can bundle together. A std::array would restrict you to use the same type for all entities, so it's out of question in most scenarii.

So when you want to pack together more entities before passing them to a function, you just use std::make_tuple.

For unpacking, the situation is more complex, but luckly there's some ready-to-use library solution: Boost.Hana offers boost::hana::unpack, which allows you to do something like this

constexpr auto add = [](auto x, auto y, auto z) {
    return x + y + z;
};
std::cout &lt;&lt; hana::unpack(hana::make_tuple(1, 2, 3), add); // prints 6

(The example uses boost::hana::tuple, but it's easy to extend Hana to make hana::unpack work for std::tuples as well.)

In this respect, std::tie and structured bindings are kind of the minimal building blocks, because they allow you to give names to the individual entities before you pass them to the desired function. With reference to the example above, you could rewrite it as

constexpr auto add = [](auto x, auto y, auto z) {
    return x + y + z;
};
auto const&amp; [a, b, c] = std::make_tuple(1, 2, 3);
std::cout &lt;&lt; add(a, b, c); // prints 6

which is bit more cumbersome, because you have to invent name that might not actually make much sense, depending on the specific case, clearly; as much clearly, if the tuple is just a collection of unrelated things which will take unrelated code paths, then maybe structured binding makes more sense.


In other languages packing and unpacking is more straightforward. Take JavaScript, where arrays can be etherogenous; packing more things just means enclosing them in a ,-separated list inside [ and ]. For unpacking, there's the ... operator. Here's native JavaScript code that corresponds to the C++ code above which uses a full-fledge libarary:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

add = x =&gt; x[0] + x[1] + x[2]
console.log(add([1, 2, 3])) // prints 6
add = (x, y, z) =&gt; x + y + z;
console.log(add(...[1, 2, 3])) // prints 6

<!-- end snippet -->


Finally, in a language like Haskell, where functions are all curried, i.e. they accept arguments one by one, packing and unpacking fundamentally coincide with uncurrying and currying respectively:

add3&#39;curried = \x y z -&gt; x + y + z -- === curry3 add3&#39;uncurried
triple = (1, 2, 3)
uncurry3 add3&#39;curried $ triple -- prints 6

add3&#39;uncurried = \(x, y, z) -&gt; x + y + z -- === uncurry3 add3&#39;curried
curry3 add3&#39;uncurried 1 2 3 -- prints 6

答案5

得分: 0

以下是不同语言中实现数组解构和结构的示例:

JavaScript(js)

解构:

const [a, b, c] = [1, 2, 3]; // 将数组解构为独立的变量a、b和c

结构:

const packedArray = [a, b, c]; // 将值打包到一个新数组中

Python(py)

解构:

a, b, c = (1, 2, 3)  # 将元组解构为独立的变量a、b和c

结构:

packed_tuple = (a, b, c)  # 将值打包到一个新元组中

C++(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};
英文:

Here's how array destructuring and structuring might be implemented in different languages:

js

Destructuring:

const [a, b, c] = [1, 2, 3]; // Unpacks the array into individual variables a, b, and c

Structuring:

const packedArray = [a, b, c]; // Packs the values into a new array

py

Destructuring:

a, b, c = (1, 2, 3)  # Unpacks the tuple into individual variables a, b, and c

Structuring:

packed_tuple = (a, b, c)  # Packs the values into a new tuple

cpp

C++ doesn't have a direct built-in mechanism for array destructuring or structuring like JavaScript or Python. However, you can achieve similar results using the following approaches:

Destructuring:
You can manually assign values from an array to individual variables.

int arr[] = {1, 2, 3};
int a = arr[0];
int b = arr[1];
int c = arr[2];

Structuring:
You can create an array or a data structure to hold the values.

int a = 1;
int b = 2;
int c = 3;
int arr[] = {a, b, c};

huangapple
  • 本文由 发表于 2023年8月9日 10:43:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/76864243.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定