Ruby中MatchData捕获组是否支持即时隐式转换为Integer类型
原生Ruby不支持在正则匹配阶段直接让MatchData返回Integer类型的捕获结果,所有正则捕获默认都是字符串类型,你原本的判断是正确的。下面提供几个比逐次map更优雅的实现方案:
方案1:利用命名捕获的局部变量特性简化写法
Ruby中命名捕获匹配成功后,会自动将捕获名作为当前上下文的局部变量生成,可以直接在单次遍历中完成匹配+转换,无需拆分两次map:
lines = ["answer 42", "question 100"] result = lines.map do |line| /(?<string>\w+) (?<integer>\d+)/ =~ line [string, integer.to_i] end # => [["answer", 42], ["question", 100]]
方案2:封装可复用的捕获转换扩展
如果这类转换场景很多,可以用Ruby的 refinements 特性扩展MatchData,既不污染全局空间,又能实现灵活的按需转换:
module MatchDataTypeConversion refine MatchData do # 按分组序号转换,分组序号从1开始(和正则分组顺序对应) def convert_captures(type_map = {}) captures.map.with_index(1) do |val, idx| type_map.key?(idx) ? val.public_send(type_map[idx]) : val end end # 按命名捕获的名称转换 def convert_named(type_map = {}) named_captures.transform_values do |key, val| type_map.key?(key.to_sym) ? val.public_send(type_map[key.to_sym]) : val end end end end # 使用时引入扩展即可 using MatchDataTypeConversion line = "answer 42" # 普通分组转换 p line.match(/(\w+) (\d+)/).convert_captures(2 => :to_i) # => ["answer", 42] # 命名分组转换 p line.match(/(?<string>\w+) (?<integer>\d+)/).convert_named(integer: :to_i) # => { "string" => "answer", "integer" => 42 }
方案3:极简单次解构转换
如果不想写扩展,也可以用then方法在单次遍历中完成解构和转换:
result = lines.map do |line| line.match(/(\w+) (\d+)/).captures.then { [_1, _2.to_i] } end
内容的提问来源于stack exchange,提问作者Jochem Schulenklopper
相关产品推荐
相关产品推荐

