ルビー3.0の出現-日04 -パスポート処理
27893 ワード
ルビー3.0はちょうどリリースされたので、我々は実験に時間がかかると、すべての楽しい新機能がそこにあるかを参照してください年の陽気な時間です.
この一連の投稿では、Ruby 2.7と3.0の機能をいくつか紹介しますsolve Advent of Code problems . ソリューション自体は、機能の新しい使用を示すために最も効率的であることを意味していません.
これらのポストの各々が次第に長く、より関与するように、私は個々の日までにこれらを壊しています.
そう言ってしまおうじゃないか
畝
Day 04 - パート01 -パスポート処理
これは、最後のものよりかなり簡単でした.いくつかの矛盾した形式のデータを
解決策を見てみましょう.
何か?
有効なキーとマッピング
まず最初にしたいことは、すべての有効なパスポートのリストを確認することです.
今では私たちをもたらす
パターンマッチングの理由で再びシンボルキーに変換することに気づくかもしれません.
パターンに織られる
では、次のようになります.
なぜ
これらの値を直接必要としない間、デバッグのために見たり、必要に応じて後で変更する場合には、非常に有用です.
カウント
それから、私たちは、カウントを得るために上記の機能をラップするために、私たちの古い無限の機能トリックにダウンします
異なるものに分割する
あなたは私が使用しなかったことに気づくかもしれない
...そして、それで、我々は日4のパート1のために我々の解決をします.
日04 -パート02 -パスポートの検証
Part 2はかなりの数の検証を加え、Rubyのすべての種類の興味深い機能を試す機会を与えてくれます.解決策を見て始めましょう、これは旅行です.
すぐ閉鎖
この行はクロージャと呼ばれます.
この設定を使用して検証の定数を設定します.
いくつかのクイックregex
最初の検証定数のセットを見てみましょう.
ここの面白いものは
楽しい構成
今、この1つは確かに少しoverkillよりも行くが、それにもかかわらず、概念を実証するために楽しいです:
関数を作成した後のシンボル、または一緒に置きます.それは最初に通過します、そして、それから第1の出力は第2を通して行きます、そして、同様に.
番目のラインは、最初の可能性がありますリターンに対して警戒している
三行目のパターンマッチングのキーを象徴する私たちのトリックに戻ります.
第4は、それが完全に少しおもしろくなるところです
我々がaに供給するならば、一緒にこれを持ってくること
一緒にもたらす
今、この解決策は、パターンマッチの明確なラインに私たちをもたらします.何も他の何も変化していないが、これは?これは私に若干の喜びをもたらします.
インライン式を使用できないことに注意してください.ピンとparensで可能なバグレポートがあります.
それはパート2を包みます、そして、それは旅行実験、そして、私がとても楽しんだ何かでした.
日04ラップ
それが4日目に包まれることについて、我々はこれらの問題の各々を通して働き続けて、次の数日と数週にわたって彼らの解決と方法論を調査し続けるでしょう.
場合は、元のソリューションのすべてをチェックアウトするthe Github repo 完全にコメントソリューションです.
畝
この一連の投稿では、Ruby 2.7と3.0の機能をいくつか紹介しますsolve Advent of Code problems . ソリューション自体は、機能の新しい使用を示すために最も効率的であることを意味していません.
これらのポストの各々が次第に長く、より関与するように、私は個々の日までにこれらを壊しています.
そう言ってしまおうじゃないか
畝
Day 04 - パート01 -パスポート処理
これは、最後のものよりかなり簡単でした.いくつかの矛盾した形式のデータを
key:value
そして、妥当性検査はすべての必要なキーが存在することを確認することです.解決策を見てみましょう.
require 'any'
VALID_KEYS = %i(byr iyr eyr hgt hcl ecl pid cid)
VALID_KEYS_MAP = VALID_KEYS.to_h { |k| [k, Any] }
VALID_NORTH_POLE_MAP = VALID_KEYS_MAP.except(:cid)
def valid_passports(passports)
passports.filter_map do |passport|
parsed_passport = passport
.split
.to_h { _1.split(':') }
.transform_keys(&:to_sym)
parsed_passport if parsed_passport in VALID_NORTH_POLE_MAP
end
end
def valid_passport_count(...) = valid_passports(...).size
File.read(ARGV[0]).split(/\n\n/).then { puts valid_passport_count(_1) }
何か?
any
何かについて比較したときに、本当に応える私自身の作りの宝石です、しかし、特に===
. パターンマッチング用途===
これは、次のように動作します.{ a: 1 } in a: Any
なぜ?何故なら、私たちは常に真実のものが欲しいからです.実験から、これは仕事にダイナミックであるパターンを得る唯一のきれいな方法です.有効なキーとマッピング
まず最初にしたいことは、すべての有効なパスポートのリストを確認することです.
VALID_KEYS = %i(byr iyr eyr hgt hcl ecl pid cid)
...そして、その後、我々Any
キーの任意のセットに対するパターンマッチングについて何か楽しいことを示すトリックVALID_KEYS_MAP = VALID_KEYS.to_h { |k| [k, Any] }
こうすることで、次のようなことができます.some_hash in VALID_KEYS_MAP
...または、この検証の場合、私たちは:cid
, そしてRuby 3はRailsランドから新しい機能を提供します.except
:VALID_NORTH_POLE_MAP = VALID_KEYS_MAP.except(:cid)
どのような場合は、1つまたは1つのリストを除いてすべてのキーを考えるかもしれません.今では私たちをもたらす
valid_passports
.to_h
キーペアto_h
Ruby 2.7の関数を使用します.map.to_h
:def valid_passports(passports)
passports.filter_map do |passport|
parsed_passport = passport
.split
.to_h { _1.split(':') }
# ...
それで、そのファイルのすべてのそれらの任意のkeypair?split
Whitespaceによって行きます.そして、それがフォーマット視差を扱うことができることを意味します、そして、我々はまっすぐにそれを供給することができますto_h
我々が欲しいハッシュにそれを作るために.パターンマッチングの理由で再びシンボルキーに変換することに気づくかもしれません.
.transform_keys(&:to_sym)
パターンに織られる
では、次のようになります.
parsed_passport if parsed_passport in VALID_NORTH_POLE_MAP
...はparsed_passport
すべての有効な北極のキーが存在します.与えられたキーの比較を行うことができましたが、Ruby 3.0デモは今のところ優先順位があります.なぜ
parsed_passport if
? 私たちはfilter_map
これは、私たちが有効なパスポートを保つだけでなく、新たに逆シリアル化(ハッシュ化)形式でそれらを保つことができます.これらの値を直接必要としない間、デバッグのために見たり、必要に応じて後で変更する場合には、非常に有用です.
カウント
それから、私たちは、カウントを得るために上記の機能をラップするために、私たちの古い無限の機能トリックにダウンします
def valid_passport_count(...) = valid_passports(...).size
異なるものに分割する
あなたは私が使用しなかったことに気づくかもしれない
readlines
こちらFile.read(ARGV[0]).split(/\n\n/).then { puts valid_passport_count(_1) }
これは、各レコードに空白行があるか、または\n\n
, それらの間に.改行に分割すれば、混乱したレコードの断片が得られるだろう.これで、我々は我々の機能にまっすぐに記録を供給することができます、そして、離れて、我々は行きます....そして、それで、我々は日4のパート1のために我々の解決をします.
日04 -パート02 -パスポートの検証
Part 2はかなりの数の検証を加え、Rubyのすべての種類の興味深い機能を試す機会を与えてくれます.解決策を見て始めましょう、これは旅行です.
require 'any'
str_int_within = -> range { -> v { range.cover? v.to_i } }
HEIGHT_REGEX = /(?<n>\d+) ?(?<units>cm|in)/
HAIR_COLOR_REGEX = /^\#[0-9a-z]{6}$/
PASSPORT_ID_REGEX = /^[0-9]{9}$/
EYE_COLOR_REGEX = Regexp.union(*%w(amb blu brn gry grn hzl oth))
VALID_BIRTH_YEAR = str_int_within[1920..2002]
VALID_ISSUED_YEAR = str_int_within[2010..2020]
VALID_EXPIRATION_YEAR = str_int_within[2020..2030]
VALID_CM_HEIGHT = str_int_within[150..193]
VALID_IN_HEIGHT = str_int_within[59..76]
VALID_HEIGHT =
-> { HEIGHT_REGEX.match(_1) } >>
-> { _1&.named_captures } >>
-> { _1&.transform_keys(&:to_sym) } >>
-> {
case _1
in units: 'cm', n: VALID_CM_HEIGHT
Any
in units: 'in', n: VALID_IN_HEIGHT
Any
else
nil
end
}
def valid_passports(passports)
passports.filter_map do |passport|
parsed_passport =
passport
.split
.to_h { _1.split(':') }
.transform_keys(&:to_sym)
parsed_passport if parsed_passport in {
byr: VALID_BIRTH_YEAR,
iyr: VALID_ISSUED_YEAR,
eyr: VALID_EXPIRATION_YEAR,
hgt: VALID_HEIGHT,
hcl: HAIR_COLOR_REGEX,
ecl: EYE_COLOR_REGEX,
pid: PASSPORT_ID_REGEX
}
end
end
def valid_passport_count(...) = valid_passports(...).size
File.read(ARGV[0]).split(/\n\n/).then { puts valid_passport_count(_1) }
今、ここで多くの楽しみ、多くの楽しいことを探索するので、それを取得しましょう.すぐ閉鎖
この行はクロージャと呼ばれます.
str_int_within = -> range { -> v { range.covers? v.to_i } }
関数がなぜ関数を返すのか?とても便利だから!ちょっと簡単なものから始めましょう.adds = -> a { -> b { a + b } }
コールすると、呼び出したものを記憶する関数を返します.a
:adds_3 = adds[3]
adds_3[3]
# => 6
他の関数に渡すこともできます.[1, 2, 3].map(&adds[3])
# => [4, 5, 6]
だからstr_int_within
, 文字列として表される数が数年の範囲内か数であるかを調べることができる関数を求めます.str_int_within = -> range { -> v { range.covers? v.to_i } }
最初の定数を見てみましょう.VALID_BIRTH_YEAR = str_int_within[1920..2002]
以下のようにテストできます:VALID_BIRTH_YEAR['1800']
# => false
VALID_BIRTH_YEAR['2000']
# => true
array_of_birth_years.select(&VALID_BIRTH_YEAR)
したがって、この概念は、実際に柔軟性があり、さらにパターンマッチングで使用することができます.クロージャについてもっと知りたい場合はgive this article a read .この設定を使用して検証の定数を設定します.
VALID_BIRTH_YEAR = str_int_within[1920..2002]
VALID_ISSUED_YEAR = str_int_within[2010..2020]
VALID_EXPIRATION_YEAR = str_int_within[2020..2030]
VALID_CM_HEIGHT = str_int_within[150..193]
VALID_IN_HEIGHT = str_int_within[59..76]
いくつかのクイックregex
最初の検証定数のセットを見てみましょう.
HEIGHT_REGEX = /(?<n>\d+) ?(?<units>cm|in)/
HAIR_COLOR_REGEX = /^\#[0-9a-z]{6}$/
PASSPORT_ID_REGEX = /^[0-9]{9}$/
EYE_COLOR_REGEX = Regexp.union(*%w(amb blu brn gry grn hzl oth))
なぜ定数?これらの検証名を与えて、何をするかを説明できます.ここの面白いものは
Regexp.union
これにより、複数の正規表現や文字列を結合することができます.我々が使用することを許可include?
, でもArray
に応答しません===
パターンマッチングのための非理想化regexは以下のようにします./abc/ === 'abc'
# => true
case something
when /abc/ then true
else false
end
それでcase / when
and case / in
値と一致するように動作します.===
. 気の利いたもの、そして恥Array
and Hash
実装しないでください.楽しい構成
今、この1つは確かに少しoverkillよりも行くが、それにもかかわらず、概念を実証するために楽しいです:
VALID_HEIGHT =
-> { HEIGHT_REGEX.match(_1) } >>
-> { _1&.named_captures } >>
-> { _1&.transform_keys(&:to_sym) } >>
-> {
case _1
in units: 'cm', n: VALID_CM_HEIGHT
Any
in units: 'in', n: VALID_IN_HEIGHT
Any
else
nil
end
}
最初の行はHEIGHT_REGEX
上から、それが有効な高さであるかどうかチェックしてくださいMatchData
とunit
そして、何があるか.関数を作成した後のシンボル、または一緒に置きます.それは最初に通過します、そして、それから第1の出力は第2を通して行きます、そして、同様に.
番目のラインは、最初の可能性がありますリターンに対して警戒している
nil
孤独な演算子を使用することによって&.
) 返り値nil
呼び出すならばnil
, またはいくつかの有効な場合は、実際の関数を呼び出してMatchData
仕事をする.三行目のパターンマッチングのキーを象徴する私たちのトリックに戻ります.
第4は、それが完全に少しおもしろくなるところです
case / in
パターンマッチ:case _1
in units: 'cm', n: VALID_CM_HEIGHT
Any
in units: 'in', n: VALID_IN_HEIGHT
Any
else
nil
end
この点で_1
当社のキャプチャグループ、またはそれはnil
. もしそうならnil
または上記の条件にマッチしませんnil
無効な高さを表す.有効なケースはもっと面白いものだ.それはunits
and n
我々の試合から、使用して値を比較します===
. この場合、これらの正規表現は上からです.我々がaに供給するならば、一緒にこれを持ってくること
String
高さを含んでいるので、それが有効であるかどうかについて、我々が一致することができて、言うことができる形式であるまで、シーケンスのすべてのそれらの機能を通過します.一緒にもたらす
今、この解決策は、パターンマッチの明確なラインに私たちをもたらします.何も他の何も変化していないが、これは?これは私に若干の喜びをもたらします.
parsed_passport if parsed_passport in {
byr: VALID_BIRTH_YEAR,
iyr: VALID_ISSUED_YEAR,
eyr: VALID_EXPIRATION_YEAR,
hgt: VALID_HEIGHT,
hcl: HAIR_COLOR_REGEX,
ecl: EYE_COLOR_REGEX,
pid: PASSPORT_ID_REGEX
}
このような形式で検証を表現できます.これは、JSONや他のデータを検証する可能性の全世界を開き、私は私の将来のためのいくつかのアイデアがあります.インライン式を使用できないことに注意してください.ピンとparensで可能なバグレポートがあります.
^(expr)
, しかし、速度に関する若干の懸念は、それとともに来ます.それはパート2を包みます、そして、それは旅行実験、そして、私がとても楽しんだ何かでした.
日04ラップ
それが4日目に包まれることについて、我々はこれらの問題の各々を通して働き続けて、次の数日と数週にわたって彼らの解決と方法論を調査し続けるでしょう.
場合は、元のソリューションのすべてをチェックアウトするthe Github repo 完全にコメントソリューションです.
畝
Reference
この問題について(ルビー3.0の出現-日04 -パスポート処理), 我々は、より多くの情報をここで見つけました https://dev.to/baweaver/advent-of-ruby-3-0-day-04-passport-processing-389oテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol