Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

フリーズとフリーズを使用するのはいつですか? Rubyで

最近では、#freezeがよく見られます Rubyコードで使用されます。しかし、なぜフリーズが使用されているのかが完全には明らかではないことがよくあります。この投稿では、開発者が変数をフリーズする最も一般的な理由を見ていきます。それぞれの理由を説明するために、Railsコードベースおよびその他の一般的なオープンソースプロジェクトからサンプルコードを抜粋しました。

不変定数の作成

Rubyでは、定数は変更可能です。少し紛らわしいですが、コードは簡単に理解できます。ここでは、文字列定数を作成し、それに別の文字列を追加しています。

MY_CONSTANT = "foo"
MY_CONSTANT << "bar"
puts MY_CONSTANT.inspect # => "foobar"

#freezeを使用することで、実際に定数である定数を作成できます。今回、文字列を変更しようとすると、RuntimeErrorが発生します。

MY_CONSTANT = "foo".freeze
MY_CONSTANT << "bar" # => RuntimeError: can't modify frozen string

これは、ActionDispatchコードベースでの実際の例です。 Railsは、ログ内の機密データを「[FILTERED]」というテキストに置き換えることで非表示にします。このテキストは凍結された定数に保存されます。

module ActionDispatch
  module Http
    class ParameterFilter
      FILTERED = '[FILTERED]'.freeze
      ...
オブジェクトの割り当てを減らす

Rubyアプリを高速化するためにできる最善のことの1つは、作成されるオブジェクトの数を減らすことです。オブジェクト割り当ての厄介な原因の1つは、ほとんどのアプリに散在している文字列リテラルにあります。

log( "foobar")のようなメソッド呼び出しを行うたびに、新しいStringオブジェクトを作成します。コードがこのようなメソッドを1秒間に数千回呼び出す場合、それは1秒間に数千の文字列を作成(およびガベージコレクション)していることを意味します。それは多くのオーバーヘッドです!

幸いなことに、Rubyは私たちに道を与えてくれます。文字列リテラルをフリーズすると、Rubyインタープリターは1つのStringオブジェクトのみを作成し、将来の使用のためにそれをキャッシュします。凍結された文字列引数と凍結されていない文字列引数のパフォーマンスを示す簡単なベンチマークをまとめました。パフォーマンスが約50%向上しています。

require 'benchmark/ips'

def noop(arg)
end

Benchmark.ips do |x|
  x.report("normal") { noop("foo") }
  x.report("frozen") { noop("foo".freeze)  }
end

# Results with MRI 2.2.2:
# Calculating -------------------------------------
#               normal   152.123k i/100ms
#               frozen   167.474k i/100ms
# -------------------------------------------------
#               normal      6.158M (± 3.3%) i/s -     30.881M
#               frozen      9.312M (± 3.5%) i/s -     46.558M

Railsルーターを見ると、これが実際に動作していることがわかります。ルーターはすべてのWebページ要求に使用されるため、高速である必要があります。これは、多くの凍結された文字列リテラルを意味します。

# excerpted from https://github.com/rails/rails/blob/f91439d848b305a9d8f83c10905e5012180ffa28/actionpack/lib/action_dispatch/journey/router/utils.rb#L15
def self.normalize_path(path)
  path = "/#{path}"
  path.squeeze!('/'.freeze)
  path.sub!(%r{/+\Z}, ''.freeze)
  path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
  path = '/' if path == ''.freeze
  path
end

Rubyに組み込まれた最適化>=2.2

Ruby 2.2以降(MRI)は、ハッシュキーとして使用される文字列リテラルを自動的にフリーズします。

user = {"name" => "george"}

# In Ruby >= 2.2
user["name"]

# ...is equivalent to this, in Ruby <= 2.1
user["name".freeze]

また、Matzによると、Ruby3ではすべての文字列リテラルが自動的にフリーズされます。

値オブジェクトと関数型プログラミング

Rubyは関数型プログラミング言語ではありませんが、多くのRubyistは、関数型で作業することの価値を認識し始めています。このスタイルの主な信条の1つは、副作用を回避する必要があるということです。初期化された後、オブジェクトが変更されることはありません。

コンストラクター内でfreezeメソッドを呼び出すことにより、オブジェクトが決して変更されないことを保証できます。意図しない副作用があると、例外が発生します。

class Point
  attr_accessor :x, :y
  def initialize(x, y)
    @x = x
    @y = y
    freeze
  end

  def change
    @x = 3
  end
end

point = Point.new(1,2)
point.change # RuntimeError: can't modify frozen Point

  1. RubyでStructとOpenStructを使用する方法

    Rubyの構造体とは何ですか? 構造体は組み込みのRubyクラスであり、値オブジェクトを生成する新しいクラスを作成するために使用されます。値オブジェクトは、関連する属性を一緒に格納するために使用されます。 ここに例があります : Point 2つの座標(x &y 。 このデータはさまざまな方法で表すことができます。 いいね : 配列[10, 20] ハッシュ{ x: 10, y: 10 } オブジェクトPoint.new(10, 20) 複数のPointを使用する場合 、オブジェクトアプローチを使用することをお勧めします。 しかし… これら2つの値を一緒に格納するた

  2. Ruby文字列のフォーマット

    Rubyで文字列をフォーマットする方法について話しましょう。 なぜ文字列をフォーマットしたいのですか?数値が10未満であっても、先行ゼロを使用したり(例:01、02、03…)、コンソール出力を列に適切にフォーマットしたりすることができます。 他の言語では、 printfを使用できます 文字列をフォーマットする関数です。Cを使用したことがある場合は、おそらくそのことに精通しているでしょう。 printfを使用するには フォーマット指定子のリストと変数または値のリストを定義する必要があります。 Ruby文字列フォーマット入門 sprintf はRubyでも利用できます。この投稿では、よ