Rubyにおけるパターンマッチング入門
22542 ワード
Rubyでのパターンマッチングに関する簡単な議論から始めましょう.
あなたが数年前私のような何かであるならば、あなたはRegexでパターンマッチングでそれを混同するかもしれません.他のどの文脈も「パターンマッチング」の速いGoogle検索でさえ、あなたにその定義にかなり近い内容をもたらします.
正式に、パターンマッチングは他のデータに対してどんなデータ(文字列、一連のトークン、タプル、または他の何か)であるかをチェックするプロセスです.
プログラミングに関しては、言語の能力に応じて、以下のいずれかを意味します. 予想されるデータ型に対するマッチング 予想されるハッシュ構造に対するマッチング 予想される配列長に対するマッチング いくつかの変数にマッチ(またはその一部)を割り当てる パターンマッチングへの私の最初の進出は、エリクシールを通してでした.エリクシールfirst class support パターンマッチングのために
つまり、エリクシールでは以下のコードが有効です.
Rubyパターンマッチング
あなたが提供しないならば
ルビーにおけるパターンマッチングアレイ
パターンマッチングを使用して、データ型、長さまたは値に対して事前に必要な構造に配列を一致させることができます.
たとえば、次のすべてが一致する
エリクサーの世界では、
ここではより良い読みやすさのためにそれを使用する方法です
ルビーにおけるパターンマッチングオブジェクト
Rubyのオブジェクトにマッチして特定の構造を強制することもできます.
たとえば、空想的なグリッターを書いているならば、以下の(強く議論された)構造を持つことができます.
ルビーにおける変数結合とピン止め
上記の例のいくつかに見られるように、パターンマッチングは、パターンの一部を任意の変数に割り当てる際に非常に有用です.
これは変数結合と呼ばれ、変数にバインドできるいくつかの方法があります. 強力な型マッチで、例えば. 型指定なしで、例えば. 変数名がなければ、キー名を使用します. バインド残り. どのようにして、既存の変数をサブパターンとして使用したいときに、我々はマッチすることができますか?これは、
これは時々有用です.
しかし、ほとんどの場合、これは微妙なバグの原因である可能性がありますので、マッチの中で使用されている影付き変数値に依存しないことを確認してください.
たとえば、次のように、あなたは都市が「アムステルダム」であると思っています、しかし、それは代わりに「ベルリン」であるでしょう
Rubyカスタムクラスのマッチング
Rubyでカスタムクラスのパターンマッチングを認識する特別なメソッドを実装できます.
例えば、パターンを設定するには
これは、それらのすべてが高価である場合、受信機が必要なキーだけを提供する方法です.
同じように
例えば、私たちには
複雑なパターンのためのガードの使用
正規パターンマッチ演算子では表現できない複雑なパターンがあれば、
パターンマッチング
例として、このパターンは、管理者のみを許可するベースコントローラ内でうまく適合できます.
ルビーでのパターンマッチング
まず,パターンマッチングは,把握するのに少し奇妙な感じがする.
いくつかの場合、それはglorifiedされたオブジェクト/配列deconstructionのように感じるかもしれません.
しかし、エリクサーの人気が任意の指示である場合は、パターンマッチングはあなたの武器で持っている素晴らしいツールです.
Elixirでそれを使用して最初の手の経験を持って、私はそれが慣れて一度はそれが生活をするのは難しいことを確認することができます.
Ruby 2.7では、
警告は無効にすることができます
Rubyでのパターンマッチングはまだ初期段階にありますが、私はあなたがこの導入に有用であることを発見し、私は将来の開発については興奮していることを願って!
P . S .あなたが彼らがプレスから降りるとすぐに、ルビー魔法のポストを読みたいならば.subscribe to our Ruby Magic newsletter and never miss a single post !
私たちのゲストの著者Pulkitシニアシニアスタックエンジニアとコンサルタントです.彼の自由な時間に、彼は彼の経験について書きますhis blog .
あなたが数年前私のような何かであるならば、あなたはRegexでパターンマッチングでそれを混同するかもしれません.他のどの文脈も「パターンマッチング」の速いGoogle検索でさえ、あなたにその定義にかなり近い内容をもたらします.
正式に、パターンマッチングは他のデータに対してどんなデータ(文字列、一連のトークン、タプル、または他の何か)であるかをチェックするプロセスです.
プログラミングに関しては、言語の能力に応じて、以下のいずれかを意味します.
=
演算子は、実際にはmatch
演算子.単純な代入ではなく.つまり、エリクシールでは以下のコードが有効です.
iex> x = 1
iex> 1 = x
これを念頭に置いて、Ruby 2.7 +の新しいパターンマッチングサポートを見てみましょう.また、今日からスタートして、コードをより読みやすくするために、どのように使うことができますか.Rubyパターンマッチング
case
/in
Ruby特別なパターンマッチングをサポートcase
/in
エクスプレッション.構文は以下の通りです:case <expression>
in <pattern1>
# ...
in <pattern2>
# ...
else
# ...
end
これは、case
/when
エクスプレッション.when
and in
枝は1つで混合できないcase
.あなたが提供しないならば
else
式が失敗した場合、NoMatchingPatternError
.ルビーにおけるパターンマッチングアレイ
パターンマッチングを使用して、データ型、長さまたは値に対して事前に必要な構造に配列を一致させることができます.
たとえば、次のすべてが一致する
in
を評価するcase
最初のマッチを見て停止する)case [1, 2, "Three"]
in [Integer, Integer, String]
"matches"
in [1, 2, "Three"]
"matches"
in [Integer, *]
"matches" # because * is a spread operator that matches anything
in [a, *]
"matches" # and the value of the variable a is now 1
end
この型のパターンマッチング句は、メソッドコールから複数のシグナルを生成したい場合に非常に便利です.エリクサーの世界では、
:ok
結果と結果:error
たとえば、データベースに挿入します.ここではより良い読みやすさのためにそれを使用する方法です
def create
case save(model_params)
in [:ok, model]
render :json => model
in [:error, errors]
render :json => errors
end
end
# Somewhere in your code, e.g. inside a global helper or your model base class (with a different name).
def save(attrs)
model = Model.new(attrs)
model.save ? [:ok, model] : [:error, model.errors]
end
ルビーにおけるパターンマッチングオブジェクト
Rubyのオブジェクトにマッチして特定の構造を強制することもできます.
case {a: 1, b: 2}
in {a: Integer}
"matches" # By default, all object matches are partial
in {a: Integer, **}
"matches" # and is same as {a: Integer}
in {a: a}
"matches" # and the value of variable a is now 1
in {a: Integer => a}
"matches" # and the value of variable a is now 1
in {a: 1, b: b}
"matches" # and the value of variable b is now 2
in {a: Integer, **nil}
"does not match" # This will match only if the object has a and no other keys
end
これは素晴らしい任意のparamsに対するマッチングのための強力なルールを課すときに動作します.たとえば、空想的なグリッターを書いているならば、以下の(強く議論された)構造を持つことができます.
def greet(hash = {})
case hash
in {greeting: greeting, first_name: first_name, last_name: last_name}
greet(greeting: greeting, name: "#{first_name} #{last_name}")
in {greeting: greeting, name: name}
puts "#{greeting}, #{name}"
in {name: name}
greet(greeting: "Hello", name: name)
in {greeting: greeting}
greet(greeting: greeting, name: "Anonymous")
else
greet(greeting: "Hello", name: "Anonymous")
end
end
greet # Hello, Anonymous
greet(name: "John") # Hello, John
greet(first_name: "John", last_name: "Doe") # Hello, John Doe
greet(greeting: "Bonjour", first_name: "John", last_name: "Doe") # Bonjour, John Doe
greet(greeting: "Bonjour") # Bonjour, Anonymous
ルビーにおける変数結合とピン止め
上記の例のいくつかに見られるように、パターンマッチングは、パターンの一部を任意の変数に割り当てる際に非常に有用です.
これは変数結合と呼ばれ、変数にバインドできるいくつかの方法があります.
in [Integer => a]
or in {a: Integer => a}
in [a, 1, 2]
or in {a: a}
. in {a:}
変数名a
で値をキーa
. in [Integer, *rest]
or in {a: Integer, **rest}
. ^
( pin )演算子:a = 1
case {a: 1, b: 2}
in {a: ^a}
"matches"
end
変数がパターン自体で定義されている場合にも、このように強力なパターンを書くことができます.case order
in {billing_address: {city:}, shipping_address: {city: ^city}}
puts "both billing and shipping are to the same city"
else
raise "both billing and shipping must be to the same city"
end
変数結合に関して言及する1つの重要なquirkは、パターンが完全にマッチしないとしても、変数がまだ縛られたということです.これは時々有用です.
しかし、ほとんどの場合、これは微妙なバグの原因である可能性がありますので、マッチの中で使用されている影付き変数値に依存しないことを確認してください.
たとえば、次のように、あなたは都市が「アムステルダム」であると思っています、しかし、それは代わりに「ベルリン」であるでしょう
city = "Amsterdam"
order = {billing_address: {city: "Berlin"}, shipping_address: {city: "Zurich"}}
case order
in {billing_address: {city:}, shipping_address: {city: ^city}}
puts "both billing and shipping are to the same city"
else
puts "both billing and shipping must be to the same city"
end
puts city # Berlin instead of Amsterdam
Rubyカスタムクラスのマッチング
Rubyでカスタムクラスのパターンマッチングを認識する特別なメソッドを実装できます.
例えば、パターンを設定するには
first_name
and last_name
, 我々は定義することができますdeconstruct_keys
クラスで:class User
def deconstruct_keys(keys)
{first_name: first_name, last_name: last_name}
end
end
case user
in {first_name: "John"}
puts "Hey, John"
end
The keys
引数deconstruct_keys
パターンに要求されたキーを含みます.これは、それらのすべてが高価である場合、受信機が必要なキーだけを提供する方法です.
同じように
deconstruct_keys
, の実装を提供するdeconstruct
オブジェクトを配列としてパターンマッチできるようにする.例えば、私たちには
Location
緯度と経度を持つクラス.使用に加えてdeconstruct_keys
緯度と経度キーを提供するために、私たちは[latitude, longitude]
同様にclass Location
def deconstruct
[latitude, longitude]
end
end
case location
in [Float => latitude, Float => longitude]
puts "#{latitude}, #{longitude}"
end
複雑なパターンのためのガードの使用
正規パターンマッチ演算子では表現できない複雑なパターンがあれば、
if
(or unless
) 文にマッチするガードを提供するcase [1, 2]
in [a, b] if b == a * 2
"matches"
else
"no match"
end
パターンマッチング
=>
/in
なしでcase
ルビー3 +の場合は、さらに多くのパターンに魔法に一致するアクセスがあります.Ruby 3から始めて、case文なしで1行でパターンマッチングを行うことができます.[1, 2, "Three"] => [Integer => one, two, String => three]
puts one # 1
puts two # 2
puts three # Three
# Same as above
[1, 2, "Three"] in [Integer => one, two, String => three]
上記の構文にはelse
データ構造が既知であるときに最も有用です.例として、このパターンは、管理者のみを許可するベースコントローラ内でうまく適合できます.
class AdminController < AuthenticatedController
before_action :verify_admin
private
def verify_admin
Current.user => {role: :admin}
rescue NoMatchingPatternError
raise NotAllowedError
end
end
ルビーでのパターンマッチング
まず,パターンマッチングは,把握するのに少し奇妙な感じがする.
いくつかの場合、それはglorifiedされたオブジェクト/配列deconstructionのように感じるかもしれません.
しかし、エリクサーの人気が任意の指示である場合は、パターンマッチングはあなたの武器で持っている素晴らしいツールです.
Elixirでそれを使用して最初の手の経験を持って、私はそれが慣れて一度はそれが生活をするのは難しいことを確認することができます.
Ruby 2.7では、
case
/in
) はまだ実験的です.Ruby 3ではcase
/in
新しく導入された単一ラインパターンマッチング式が実験的である間、安定して移動しました.警告は無効にすることができます
Warning[:experimental] = false
コードまたは-W:no-experimental
コマンドラインキー.Rubyでのパターンマッチングはまだ初期段階にありますが、私はあなたがこの導入に有用であることを発見し、私は将来の開発については興奮していることを願って!
P . S .あなたが彼らがプレスから降りるとすぐに、ルビー魔法のポストを読みたいならば.subscribe to our Ruby Magic newsletter and never miss a single post !
私たちのゲストの著者Pulkitシニアシニアスタックエンジニアとコンサルタントです.彼の自由な時間に、彼は彼の経験について書きますhis blog .
Reference
この問題について(Rubyにおけるパターンマッチング入門), 我々は、より多くの情報をここで見つけました https://dev.to/appsignal/an-introduction-to-pattern-matching-in-ruby-1bciテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol