- 投稿日:2022-04-02T21:57:33+09:00
定数だけを切り出したファイルを作り、複数クラスで使えるようにする
やりたいこと Rubyで定義した定数を他のクラスでも使いたいとき、クラス名::定数名のように書くと思う。 ほとんどの場合は問題ないけれど、ときどき下記のような違和感や問題を感じるときがある。 関連が強いor内容が似てる複数のクラスで同じ定数を使いたいとき、特定のクラスにだけ定数を記載し、他のクラスではクラス名::定数名と書くことへの違和感 クラス名が長いときなどに読みづらくなる なのでより読みやすい、使いやすい形にしたかった。 ( config/initializers/constants.rbで共通の定数を設定する方法もあるが、そこまで全体的に使いたい訳でもない場合を想定 ) どうするか 定数用のmoduleを作って、定数を使いたいクラスにそのmoduleを読み込んで使う やりかた moduleを作成し、定数を記載する 使いたいクラスで読み込む ① moduleを作って定数を書く HogeConstants.rbを作成し、定数を書く module HogeConstants NEKO = "neko".freeze! INU = "inu".freeze! DORAEMON = "doraemon".freeze! end ②使いたいクラスで読み込む class Hoge < ApplicationRecord require "hoge_constants" include HogeConstants end moduleはincludesを使わないとエラー出るので注意。 これで複数のクラスで「クラス名::」を書かなくても定数が使える。 もっといい方法や、ツッコミあったら是非いただきたいです?
- 投稿日:2022-04-02T19:49:52+09:00
AtCoder ABC の自分の参加状況を調べてみる
概要 わたしは AtCoder の過去問をテキトーに解くだけでやっているうち、どの問題をどれだけ解いたのかわからなくなってしまいました。なので、スクレイピングで過去の ABC の解答状況を表示させるコードを書いてみました。 公式 API があればいちばんよいのでしょうが、どうもないようなので、Ruby を使ってスクレイピングしています。問題があればコメント欄で御指摘願います。 結果 ABC245 B - Mex (Elixir (1.10.2)) D - Polynomial division (Elixir (1.10.2)) C - Choose Elements (Elixir (1.10.2)) A - Good morning (Elixir (1.10.2)) D - Polynomial division (Ruby (2.7.1)) C - Choose Elements (Ruby (2.7.1)) B - Mex (Ruby (2.7.1)) A - Good morning (Ruby (2.7.1)) ABC244 D - Swap Hats (Elixir (1.10.2)) C - Yamanote Line Game (Elixir (1.10.2)) B - Go Straight and Turn Right (Elixir (1.10.2)) A - Last Letter (Elixir (1.10.2)) D - Swap Hats (Ruby (2.7.1)) C - Yamanote Line Game (Ruby (2.7.1)) B - Go Straight and Turn Right (Ruby (2.7.1)) A - Last Letter (Ruby (2.7.1)) ...... ABC213 D - Takahashi Tour (Ruby (2.7.1)) B - Booby Prize (Ruby (2.7.1)) C - Reorder Cards (Ruby (2.7.1)) A - Bitwise Exclusive Or (Ruby (2.7.1)) ABC212 D - Querying Multiset (Ruby (2.7.1)) C - Min Difference (Ruby (2.7.1)) B - Weak Password (Ruby (2.7.1)) A - Alloy (Ruby (2.7.1)) ABC211 ABC210 ABC209 ABC208 ...... こんな感じになります。ABC211 以前は解いていないのかな、って感じになっています。なお、表示されるのは「AC」を取ったものだけです。 前提 gem "mechanize" をインストールして下さい。Ruby 3.1.1 で確認しましたが、サポートされているバージョンなら別に問題なく実行できると思います。 コード 自分の「ユーザ名」と「パスワード」を.value = のあとに埋めて下さい。また、コンテストは ABC245 以前に対応しているので、定数LatestABCContestを適宜変更して下さい。 atcoder_check.rb require "mechanize" LatestABCContest = 245 agent = Mechanize.new #login url = "https://atcoder.jp/login?continue=https%3A%2F%2Fatcoder.jp%2F" page = agent.get(url) login_form = page.forms[1] login_form.field_with(name: 'username').value = '自分のユーザ名' login_form.field_with(name: 'password').value = 'パスワード' page = agent.submit(login_form) #ページ取得 define_method(:get_page) do |problem_number| num = "%03d" % problem_number puts "ABC" + num url = "https://atcoder.jp/contests/abc#{num}/submissions/me" page = agent.get(url) sleep(1) puts page.css("table tbody tr").map {|node| tds = node.css("td") if tds.empty? nil else problem = tds&.at(1)&.text language = tds&.at(3)&.text result = tds&.at(6)&.text if result == "AC" " " + problem.ljust(40) + "(#{language})" else nil end end }.compact.uniq end LatestABCContest.downto(1) { |num| get_page(num) } 見ればわかると思いますが、表示させたいコンテストを変えるには最後の1行をいじればよいです。
- 投稿日:2022-04-02T19:49:52+09:00
AtCoder ABC の自分の参加状況をスクレイピングで調べてみる
概要 わたしは AtCoder の過去問をテキトーに解くだけでやっているうち、どの問題をどれだけ解いたのかわからなくなってしまいました。なので、スクレイピングで過去の ABC の解答状況を表示させるコードを書いてみました。 公式 API があればいちばんよいのでしょうが、どうもないようなので、Ruby を使ってスクレイピングしています。問題があればコメント欄で御指摘願います。 結果 ABC245 B - Mex (Elixir (1.10.2)) D - Polynomial division (Elixir (1.10.2)) C - Choose Elements (Elixir (1.10.2)) A - Good morning (Elixir (1.10.2)) D - Polynomial division (Ruby (2.7.1)) C - Choose Elements (Ruby (2.7.1)) B - Mex (Ruby (2.7.1)) A - Good morning (Ruby (2.7.1)) ABC244 D - Swap Hats (Elixir (1.10.2)) C - Yamanote Line Game (Elixir (1.10.2)) B - Go Straight and Turn Right (Elixir (1.10.2)) A - Last Letter (Elixir (1.10.2)) D - Swap Hats (Ruby (2.7.1)) C - Yamanote Line Game (Ruby (2.7.1)) B - Go Straight and Turn Right (Ruby (2.7.1)) A - Last Letter (Ruby (2.7.1)) ...... ABC213 D - Takahashi Tour (Ruby (2.7.1)) B - Booby Prize (Ruby (2.7.1)) C - Reorder Cards (Ruby (2.7.1)) A - Bitwise Exclusive Or (Ruby (2.7.1)) ABC212 D - Querying Multiset (Ruby (2.7.1)) C - Min Difference (Ruby (2.7.1)) B - Weak Password (Ruby (2.7.1)) A - Alloy (Ruby (2.7.1)) ABC211 ABC210 ABC209 ABC208 ...... こんな感じになります。ABC211 以前は解いていないのかな、って感じになっています。なお、表示されるのは「AC」を取ったものだけです。 前提 gem "mechanize" をインストールして下さい。ruby 3.1.1p18 (2022-02-18 revision 53f5fc4236) [x86_64-linux] で確認しましたが、サポートされているバージョンなら別に問題なく実行できると思います。 コード 自分の「ユーザ名」と「パスワード」を.value = のあとに埋めて下さい。また、コンテストはここでは ABC245 以前に対応しているので、定数LatestABCContestを適宜変更して下さい。 atcoder_check.rb require "mechanize" LatestABCContest = ARGV.shift&.to_i || 245 agent = Mechanize.new #login url = "https://atcoder.jp/login?continue=https%3A%2F%2Fatcoder.jp%2F" page = agent.get(url) login_form = page.forms[1] login_form.field_with(name: 'username').value = '自分のユーザ名' login_form.field_with(name: 'password').value = 'パスワード' page = agent.submit(login_form) #ページ取得 define_method(:get_page) do |problem_number| num = "%03d" % problem_number puts "ABC" + num url = "https://atcoder.jp/contests/abc#{num}/submissions/me" page = agent.get(url) sleep(1) result = page.css("table tbody tr").map {|node| tds = node.css("td") if tds.empty? nil else problem = tds&.at(1)&.text language = tds&.at(3)&.text c_result = tds&.at(6)&.text if c_result == "AC" " " + problem.ljust(40) + "(#{language})" else nil end end }.compact.uniq puts result end LatestABCContest.downto(1) { |num| get_page(num) } 見ればわかると思いますが、表示させたいコンテストを変えるには最後の1行をいじればよいです。
- 投稿日:2022-04-02T16:09:11+09:00
いいね Ajaxにする
Ajaxとは? ページを更新することなくページの内容だけを入れ替えられる。 ajaxの中核を支えているのはJavacriptの技術。 デメリットもあるらしいがそれを差し引いてもメリットがあると今は理解した。 何から手を出していいのかわからない。 早速実践 ボタン my_app/app/views/favorites/_favorite.html.erb <% if current_user.like?(micropost) %> <%= button_to user_favorite_path(current_user, micropost), method: :delete remote: :true do %> <%= micropost.liked.count %> <span style = "color:red;">♡</span> <% end %> <% else %> <%= button_to user_favorites_path(current_user, micropost), method: :post remote: :true do %> <%= micropost.liked.count %> <span >♡</span> <% end %> <% end %> :remote :trueでAjaxでリンクを処理ができるらしい。 コントローラ my_app/app/controllers/favorite/favorites_controller.rb class FavoritesController < ApplicationController before_action :logged_in_user, only: [:create, :destroy] def create @micropost = Micropost.find_by(id: params[:format]) current_user.like(@micropost) respond_to do |format| format.html { redirect_to request.referer } format.js { redirect_to request.referer } end end def destroy @micropost = Micropost.find_by(id: params[:id]) current_user.unlike(@micropost) respond_to do |format| format.html { redirect_to request.referer } format.js end end end MVCのことを考えながら見てみると、 ボタン(オプション引数を使って)でコンローラに行ってビューに表示させる。 それにあっているのかな? JavaScriptが有効になっていても、まだ十分に対応できていない部分があります。 というのも、Ajaxリクエストを受信した場合は、 Railsが自動的にアクションと同じ名前を持つJavaScript用の埋め込みRuby(.js.erb)ファイル(create.js.erbやdestroy.js.erbなど)を呼び出すからです。 ユーザーをフォローしたときやフォロー解除したときにプロフィールページを更新するために、 私たちがこれから作成および編集しなければならないのは、まさにこれらのファイルです。 予想 ファイルを小分けにする理由はリアクションを早くするためなのかもしれない。 jqueryを使う CSS idで指定する ドル記号($)とCSS idを使って、DOM要素にアクセスする文法について知る必要があります。 例えばfollow_formの要素をjQueryで操作するには、次のようにアクセスします。 $("#follow_form") jQueryの文法はCSSの記法から影響を受けており、#シンボルを使ってCSSのidを指定します。 HTMLに表示させる 指定された要素の内側にあるHTMLを、引数の内容で更新します。 例えばフォロー用フォーム全体を"foobar"という文字列で置き換えたい場合は、 次のようなコードになります。 $("#follow_form").html("foobar") create.js.erbファイルでは、フォロー用のフォームをunfollowパーシャルで更新し、 フォロワーのカウントを更新するのにERbを使っています 予想 できるだけ小さいファイルにする方がいいのかな? ビュー app/views/microposts/_micropost.html.erb <li id="micropost-<%= micropost.id %>"> . . . <%= render 'microposts/favorite', micropost: micropost %> . . . </li> app/views/microposts/_favorite.html.erb <div id="like_form"> <% if current_user.like?(micropost) %> <%= render "microposts/unlike", micropost: micropost %> <% else %> <%= render "microposts/like", micropost: micropost %> <% end %> </div> app/views/microposts/_unlike.html.erb <%= button_to user_favorite_path(current_user, micropost), method: :delete ,remote: true do %> <%= micropost.liked.count %> <span style = "color:red;">♡</span> <% end %> app/views/microposts/_like.html.erb <%= button_to user_favorites_path(current_user, micropost), method: :post, remote: true do %> <%= micropost.liked.count %> <span >♡</span> <% end %> エラー ActionController::UnknownFormat app/views/favorites/create.js.erb $("#like_form").html("<%= escape_javascript(render 'microposts/like', micropost: @micropost) %>"); app/views/favorites/destroy.js.erb $("#like_form").html("<%= escape_javascript(render 'microposts/unlike', micropost: @micropost) %>"); できなかった。 current_user.like?(miropost)でcurrent_userがうまく機能しないらしい。 これからまた勉強し直して必ず身を結べるように努力する
- 投稿日:2022-04-02T10:22:49+09:00
Crystalにさわり始めてから半年ぐらいの人が感じたRubyとCrystalの違い
こんにちは。 初心者の所感というのは意外と大切で、初心者でなくなってしまうと得られた知識が空気のようになってしまい、忘れられてしまうことがあります。なので、Crystalにさわりはじめてから半年ぐらいの段階で、Rubyとここが違うなと感じたことをメモしておこうと思います。基本的にポエムです。 大クラス主義の配列とHashがないけどなんとかなる Rubyだと、大クラス主義の配列がいろいろな役目を果たしてくれます。配列に異なるクラスのデータを詰め込んで、あとから簡単に追加したり削除することができます。Crystalだとできません。 これは実はそこまで大きな問題ではなくて、記述量は多くなりますが、雑多なクラスを配列やHashに詰め込む変わりに、自作のクラスを定義したり、構造体を使ったり、タプルを使うことでだいたい解決できると感じます。 evalが使えない わたし、よくわかってないんですが、直感的にはこれがRubyとCrystalの本質的な違いではないかという予感がしています。CrystalでEvalを実行するためには、Crsytal本体を実行ファイルに含めて、都度コンパイルするような必要があると思います。それはおそらく今後も不可能です。 Crystal言語は実行速度はたいへん高速ですが、コンパイル時間が短いとは言い難い言語です。ですから、Crsytalコンパイラを実行ファイルに含めて、その都度コンパイルするようにすると相当遅くなりそうな気がします。実際どうなんだろうか。あと、Ruby言語ではevalの多用はバッドプラクティスとして嫌われていますが、プログラミング学習のためにはむしろevalを多用するコードを書いてみた方がいいのかも知れないなと思いました。 戻り値の型を1種類にするか、複数にするか メソッドを実装する時、Rubyの場合は戻り値は多くの型を取るようにするのが親切です。ところがCrystalでは1つの型だけ返す方が便利だと感じます。例えば、alignment.tag("TAG") のようなメソッドを考えたとき、Rubyの場合、戻り値の型はタグに応じて Integer、Float、String、Nil を取るとしても、Crystalでは alignment.tag_int( "TAG1") alignment.tag_float("TAG2") alignment.tag_string("TAG3") といったように、それぞれの型を返すメソッドを別に用意した方が親切であると感じます。 利用者は自分が取りたい型を知っているので、使わないとわかってる型のために余計な記述(たとえばオーバーロードなど)をしないとコンパイルが通らないのは苦痛です。とすると、CrystalとRubyでは提供するべきメソッドの粒度は違うということになると思います。 今の段階でよくわかっていないと感じること ClassとStructの使い分け マクロでどこまでできるか マクロでは each ではなく for 文であるという事実 この記事は以上です。
- 投稿日:2022-04-02T08:41:16+09:00
【LeetCode】104. Maximum Depth of Binary Treeを解いてみた
はじめに コーディングテスト対策としてLeetCodeの104. Maximum Depth of Binary Treeを解いていく。 問題文を和訳 二分木の根を与えられて、その最大の深さを返します。 二分木の最大深度は、ルートノードから最も遠いリーフノードまでの最長パスに沿ったノードの数です。 Input: root = [3,9,20,null,null,15,7] Output: 3 回答 104_MaximumDepthofBinaryTree.rb def max_depth(root) if root return [max_depth(root.left), max_depth(root.right)].max + 1 else return 0 end end 最後に 難易度はEasyでした。