$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

VSCodeで全ワークスペースで使うdebug launch設定をする

VSCodeでデバッガを起動したい時に、毎回.vscode/launch.jsonの追加をしていた。これ面倒だなと思っていたのだが、普通にsettings.jsonのlaunchというキー名で全ワークスペースで使うdebug launch configurationの設定ができた。

例えばRubyデバッグのためにvscode-rdbgを使っていた場合、.vscode/settings.jsonに次のように設定しておくと全ての場所でVSCode上でRubyデバッグができる。便利ですね。

  "launch": {
    "version": "0.2.0",
    "configurations": [
      {
          "type": "rdbg",
          "name": "Debug current file with rdbg",
          "request": "launch",
          "script": "${file}",
          "args": [],
          "askParameters": true
      },
      {
          "type": "rdbg",
          "name": "Debug current test with rdbg",
          "request": "launch",
          "command": "rspec",
          "script": "${file}",
          "args": [],
          "askParameters": false
      }
    ]
  }

参考

優先度付きキューにも使われる二分ヒープ構造をRubyで実装してみる

アルゴリズム図鑑を眺めていて、二分ヒープ構造は優先度付きキューに使われることを知った。面白いなーと思うと同時に、そういえば二分ヒープ構造の実装をしたことがなく、あまり理解できていないことに気づいた。そこで簡単にRubyで実装をしてみたのでメモ。簡単なテストケースを作ったので多分合ってると思うけど、もしかしたらバグっているかも...

二分ヒープの詳細は二分ヒープ - Wikipediaも参考。

【2023/01/03 14:01追記】要素数が1の時に要素が空にならないバグがあったので修正しました。コメントありがとうございます。https://github.com/shibayu36/algorithms/commit/6c2ce588f7bc7fb890c6a560c7ab062c6f531a9a

実装

class Heap
  attr_reader :nodes

  def initialize(arr)
    @nodes = []
    arr.each { |val| push(val) }
  end

  def size
    @nodes.size
  end

  def push(val)
    @nodes.push(val)

    current_index = size - 1
    parent_index = parent_of(size - 1)

    while current_index > 0 && @nodes[parent_index] > @nodes[current_index]
      swap(parent_index, current_index)
      current_index = parent_index
      parent_index = parent_of(current_index)
    end
  end

  def pop
    return @nodes.pop if size <= 1

    res = @nodes[0]

    @nodes[0] = @nodes.pop

    current_index = 0
    child_index = min_child_of(current_index)

    while child_index && @nodes[child_index] < @nodes[current_index]
      swap(child_index, current_index)
      current_index = child_index
      child_index = min_child_of(current_index)
    end

    res
  end

  private

  def parent_of(index)
    (index - 1) / 2
  end

  def min_child_of(index)
    left_child_index = (2 * index) + 1
    right_child_index = (2 * index) + 2

    left_val = nodes[left_child_index]
    right_val = nodes[right_child_index]

    if left_val && right_val
      left_val <= right_val ? left_child_index : right_child_index
    elsif left_val
      left_child_index
    end
  end

  def swap(i, j)
    @nodes[i], @nodes[j] = @nodes[j], @nodes[i]
  end
end

テスト

require 'heap'

describe Heap do
  describe '#push' do
    it do
      heap = Heap.new([4, 1, 3])
      expect(heap.nodes).to eq([1, 4, 3])

      heap.push(5)
      expect(heap.nodes).to eq([1, 4, 3, 5])

      heap.push(0)
      expect(heap.nodes).to eq([0, 1, 3, 5, 4])
    end
  end

  describe '#pop' do
    it do
      heap = Heap.new([0, 1, 3, 5, 4, 3])

      expect(heap.pop).to eq 0
      expect(heap.nodes).to eq([1, 3, 3, 5, 4])

      expect(heap.pop).to eq 1
      expect(heap.nodes).to eq([3, 4, 3, 5])

      expect(heap.pop).to eq 3
      expect(heap.pop).to eq 3
      expect(heap.pop).to eq 4
      expect(heap.pop).to eq 5

      expect(heap.pop).to eq nil
    end
  end
end

これはminヒープという構造。popすると必ず値が低い順に取得することができる。例えば値に優先度を示す数字を入れておけば、優先度付きキューとして使える。便利。

Rubyでnewに渡ってきたパラメータ引数のaccessorを自動で作る

GitHub Copilotに補完されて面白いなと思ったのでメモ。たとえばLTSVのログを格納する構造を作るときに、渡ってきたパラメータ引数名を使ってアクセサを作りたいとする。以下のようなコードで実現が可能。

class Log
  # パラメータ引数をハッシュで受け取って
  def initialize(**fields)
    fields.each do |key, value|
      # インスタンス変数を作りつつ
      instance_variable_set("@#{key}", value)
      # attr_readerで動的に定義
      self.class.send(:attr_reader, key)
    end
  end
end

これを使うと、勝手にアクセサが生えてくる。

log = Log.new(
  user: 'frank',
  status: '200',
  size: '2326',
)
p(log.user) # => 'frank'
p(log.status) # => '200'
p(log.size) # => '2326'