🦁

[Feature #20349] パターンマッチでキャプチャした値を後から参照できるようにする提案

2024/03/23に公開

[Feature #20349] Pattern Matching - Expose local variable captures

  • パターンマッチでキャプチャした値を後から(外から)参照できるようにする提案
  • チケットだと以下のようなコードが提示されている
require "rubocop"

def ast_from(string)
  case string
  when String then processed_source_from(string).ast
  when RuboCop::ProcessedSource then string.ast
  when RuboCop::AST::Node then string
  else nil
  end
end

def longform_block?(node)
  node in [:block,                            # s(:block,
           [:send, target, target_method],    #   s(:send, s(:array), :select),
           [[:arg, v]],                       #   s(:args, s(:arg, :v)),
           [:send, [:lvar, ^v], block_method] #   s(:send, s(:lvar, :v), :even?))
  ]
end

longform_block?(ast_from("[1, 2, 3].select { |v| v.even? }"))
# => true

# longform_block? 内でキャプチャしたローカル変数を外から参照する
PatternMatch.last_match.transform_values(&:source)
# => {:node=>"[1, 2, 3].select { |v| v.even? }",
# :result=>"[1, 2, 3].select { |v| v.even? }",
# :target=>"[1, 2, 3]",
# :target_method=>:select,
# :v=>:v,
# :block_method=>:even?}

# 以下のように値を取得するイメージ
def longform_block?(node)
  result = node in [:block,                        # s(:block,
                    [:send, target, target_method],    #   s(:send, s(:array), :select),
                    [[:arg, v]],                       #   s(:args, s(:arg, :v)),
                    [:send, [:lvar, ^v], block_method] #   s(:send, s(:lvar, :v), :even?))
  ]

  pp(binding.local_variables.to_h { [_1, binding.local_variable_get(_1).then { |s| s.source rescue s }] })

  result
end
  • Regexp.last_match と同じような機能がほしい、って感じみたいですね
  • これを実現する場合にパターンマッチ毎に内部で値を保持するようにする必要があるのでパフォーマンスに影響がありそうだなーと思ったら同じことがコメントにもかかれていましたね
  • 個人的には binding からいい感じにローカル変数の情報を返すようにすればいいんじゃないかなー
GitHubで編集を提案

Discussion