有没有办法在 Crystal 中使用带有 splat 运算符的非 Tuple 对象?

Is there a way to use non Tuple objects with splat operator in Crystal?

是否有一些函数或语法结构可以使下一个示例正常工作?

使用 Array 参数调用 Hash#values_at 函数:

h = {"a" => 1, "b" => 2, "c" => 3}
ary = ["a", "b"]
h.values_at(*ary) # Error: argument to splat must be a tuple, not Array(String)

传递Hash初始化class或struct:

struct Point                                                                                                 
    def initialize(@x : Int32, @y : Int32)                                                                     
    end                                                                                                        
end
h = {"x" => 1, "y" => 2}
Point.new(**h) # Error: argument to double splat must be a named tuple, not Hash(String, Int32)

Array 和 Hash 是动态扩展的容器,元素的数量可能会在运行时发生变化,当您尝试展开时,数组可能为空。

Tuple 和 NamedTuple 由固定数量的元素组成,这些元素在编译时是已知的,因此它们可用于 splats。如果你的数据容器的格式没有改变,你可以直接使用 Tuple 和 NamedTuple。

ary = {"a", "b"}
h.values_at(*ary)

第一个例子可能是不可能的,这取决于具体情况。但是如果元素的长度是固定的,你可以这样做:

h = {"a" => 1, "b" => 2, "c" => 3}
ary = ["a", "b"]
p h.values_at(*{String, String}.from(ary))

https://carc.in/#/r/3oot See Tuple.from

NamedTuple 支持相同的方法:

struct Point                                                                                                 
    def initialize(@x : Int32, @y : Int32)                                                                     
    end                                                                                                        
end
h = {"x" => 1, "y" => 2}

p Point.new(**{x: Int32, y: Int32}.from(h))

https://carc.in/#/r/3oov See NamedTuple.from

这两个只是确保类型和在运行时手动分解结构的一些糖分,主要在数据来自外部源时有用,例如从 JSON.

解析

当然,在可能的情况下,首先创建和使用 Tuple 而不是 ArrayNamedTuple 而不是 Hash