Crystal中非Tuple对象能否用展开运算符?相关语法用法问询
Crystal语言相关问题解答
嘿,我来帮你逐个理清这些问题~
1. 非Tuple对象与展开运算符的配合使用
Crystal里的展开运算符*确实最常和Tuple搭配,但并不是只能跟Tuple玩!比如Array类型,在传递给可变参数方法(也就是定义时用*args接收的参数)时,直接用*array就能把数组元素展开成独立参数。举个例子:
def print_items(*items) items.each { |item| puts item } end my_array = [1, "hello", true] print_items(*my_array) # 会依次输出1、hello、true
如果是其他自定义的非Tuple对象,你可以给它实现to_tuple或者to_a方法,这样就能通过转换后再用展开运算符。比如:
struct MyCollection def initialize(@items : Array(Int32)) end def to_tuple @items.to_tuple end end coll = MyCollection.new([10, 20, 30]) a, b, c = *coll # 这里a=10,b=20,c=30
2. 使用Array参数调用Hash#values_at
Hash#values_at方法本身就支持可变参数(接收多个键),所以你只需要用*把Array展开成独立的键参数就行,非常直接:
my_hash = {name: "Alice", age: 30, city: "Beijing"} target_keys = [:name, :city] # 用*展开数组,传入values_at result = my_hash.values_at(*target_keys) puts result # 输出 ["Alice", "Beijing"]
3. 将Hash传入类/结构体的initialize方法完成初始化
有两种常用的方式来实现这个需求:
方式一:手动定义关键字参数初始化方法
先给类/结构体定义接收关键字参数的initialize,然后用**把Hash展开成关键字参数传入:
struct User property name : String, age : Int32 def initialize(**opts) @name = opts[:name] @age = opts[:age] end end user_data = {"name" => "Bob", "age" => 25} # 用**展开Hash作为关键字参数 user = User.new(**user_data) puts user.name # 输出 Bob
方式二:用宏自动生成初始化逻辑
如果类/结构体的属性很多,手动写每个属性的赋值太麻烦,可以用Crystal的宏来自动生成适配Hash的初始化方法:
struct Product property id : Int32, name : String, price : Float64, stock : Int32 macro initialize def initialize(**opts) {{ @type.instance_vars.each do |var| # 把实例变量名(比如@id)转成对应的键名"id" key = var.name.to_s[1..-1] "#{var.name} = opts[#{key.inspect}]" end.join("\n ") }} end end end product_data = {"id" => 1001, "name" => "Laptop", "price" => 5999.9, "stock" => 10} product = Product.new(**product_data) puts product.price # 输出 5999.9
这样不管有多少属性,宏都会帮你自动完成从Hash键到实例变量的赋值,非常省心~
内容的提问来源于stack exchange,提问作者Mikhail Gordeev
相关产品推荐
相关产品推荐

