20210912のRubyに関する記事は22件です。

【初歩中の初歩】Rubyで最小値を取り出そう★

今回はRubyを用いて最小値を取り出す方法をまとめます。 nに正の整数が入力されるので、そこから最小値を取り出してみましょう。 回答 array = [] 5.times do array << gets.to_i end p array.min 解説 まずは空の配列を準備します。 今回はarrayメソッドを用います。 解説 array = [] 次に、実際に入力される正の整数を配列の中に入れていきます。 今回は数字が5つ与えられていると仮定しています。 「<<」は配列の末尾に勝手に追加してくれます。 今回は整数なので、「to_i」を足して、保存型をintegerにします。 解説② 5.times do array << gets.to_i end 最後に配列の中から最小値を取り出すので、「.min」をつけます 最大値を出したい時は「.max」にします。 以上です!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby while文の使い方

while文とは While文は繰り返し処理で使うものです。 繰り返し処理とは 「〇〇回繰り返し処理をやる」 「条件を満たすまで処理をする」 みたいな処理を繰り返す処理のことです。 while文の使い方 a = 0 while(a < 200) puts a a = a + 20 end で実行するとすると 0 20 40 60 80 100 120 140 160 180 と出力される これは まず a=0 で定義します。  while(a < 200)は200より下の数まで処理がされるということです。 puts a と a + 20で 0 + 20の20を出力させます。 その後、aが20となり a+20 つまり 20 + 20が処理をされて40と出力されます。 その後、aが40となり、 a + 20 が 40 + 20が処理をされて60と出力されます。 これが200より下の数字まで続くわけです。 他にもuntilやloopnadoなど繰り返し処理は色々あるので調べてみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby 入門 [分割するメソッド]

メソッドを定義する→呼び出す def area puts 2 * 2 end area ↪️4 メソッドを定義するには、defにつづいてメソッド名を書きく。defは後につづく名前のメソッドを定義する。 定義したメソッドは、メソッド名を書くことで呼び出す(実行する)ことができる。 def area 2 * 2 end puts area ↪️4 メソッドは最後の実行結果が戻り値として呼び出し元へ返る仕組み。areaメソッドの中でputsするのをやめて、計算した結果の整数オブジェクト(ここでは4)を戻り値として返すように変更した。 def area 2 * 2 end result = area ———area が戻り値 4 で置き換わって result = 4 となる puts result ↪️4 戻り値は変数に代入することもできる。 メソッドへオブジェクトを渡す 引数を使ってオブジェクトを渡せるメソッドを定義する def area(x)————②引数の2をxへ代入 x * x ————③2 × 2を計算し、戻り値4を返す end puts area(2) ————①メソッド呼び出し(引数は2) ↪️4 メソッドを途中で終わらせる-return def thanks_and_receipt puts "ありがとうございました。" return --------- ここでメソッドが終わる puts "こちら、レシートになります。" ————この行は実行されない end thanks_and_receipt ↪️ありがとうございました。 retutnは戻り値を指定する機能もある。 def thanks_and_receipt(receipt) greeting = "ありがとうございました。" unless receipt ————receiptがfalseの時に次の行を実行  return greeting ——returnを実行し、変数greetingに代入されたオブジェクトを戻り値にする end greeting + "こちら、レシートになります。" ————“こちら、レシートになります。”を追加して end                                     戻り値にする。 puts thanks_and_receipt(true) ↪️ ありがとうございました。こちら、レシートになります。 引数を省略した時のデフォルト値 def order(item = "コーヒー") ————itemのデフォルト値に”コーヒー”を指定 "#{item}をください" end puts order ————引数を省略して呼び出すと”コーヒーをください”が返される puts order("カフェラテ") puts order("モカ") ↪️コーヒーをください カフェラテをください モカをください 変数itemにデフォルト値である”コーヒー”が代入される。引数をした場合は使われない。 引数の順番を変えられるキーワード引数 def order(item:, size:) ————引数名の後ろに:をつけるとキーワード引数になる "#{item}を#{size}サイズでください" end puts order(item: "カフェラテ",size: "ベンティ") —————引数を名前付きで指定できる puts order(size: "ベンティ",item: "カフェラテ") —————引数の順番も変えられる ↪️カフェラテをベンティサイズでください   カフェラテをベンティサイズでください キーワード引数でのデフォルト値 def order(item:, size: "ショート") ————sizeのデフォルト値に”ショート”を設定 "#{item}を#{size}サイズでください" end puts order(item: "カフェラテ") ————省略するとデフォルト値が使われる puts order(item: "カフェラテ",size: "ベンティ") ↪️カフェラテをショートサイズでください カフェラテをベンティサイズでください
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby 入門 [組で扱う-ハッシュ]

シンボル(symbol)とは シンボルはコロン記号(:)から始める。日本語ではあまり使わない。 シンボルも文字列や整数と同じようにオブジェクトの種類の一つ。シンボルのクラス名はSymbol。 ● to_sym to_symメソッドは文字列からシンボルへ交換 ● to_s to_sメソッドはシンボルから文字列へ交換 ハッシュの書き方 Ruby超入門のP148に書いてある キーと値の組を追加・削除する ハッシュへキーと値を追加する menu = {coffe:300,cafe_latte:400} menu[:mocha] = 400 p menu ↪️{:coffe=>300, :cafe_latte=>400, :mocha=>400} 存在しないキーを指定した時. menu = {coffe:300,cafe_latte:400} p menu[:tea] ↪️nil ハッシュから存在しないキーを指定して値を取得しようとすると、nilが得られる。 menu = {coffe:300,cafe_latte:400} menu.default = 0 p menu[:tea] ↪️0 存在しないキーを指定した時の値は、default=メソッドで設定することもできる。 2つのハッシュを1つにまとめる ●merge mergeメソッドは2つのハッシュを1つにまとめるときに使う。mergeメソッドは元のハッシュと、指定したハッシュを1つにまとめて新しいハッシュを作るメソッド。 coffee_menu = {coffee: 300, caffe_latte: 400} tea_menu = {tea: 300,tea_latte: 400} menu = coffee_menu.merge(tea_menu) ↪️{:coffee=>300, :caffe_latte=>400, :tea=>300, :tea_latte=>400} ハッシュからキーと値の組みを削除する ● delete menu = {coffee: 300,caffe_latte: 400} menu.delete(:caffe_latte) p menu ↪️{:coffee=>300} deleteメソッドはハッシュからキーと値の組を削除するときに使う。 ハッシュを繰り返し処理する ハッシュ.each do |キーの変数,値の変数|  繰り返し実行する処理 end menu = {"コーヒ" => 300,"カフェラテ" => 400} menu.each do |key, value| puts "#{key}は#{value}円です" end ↪️コーヒは300円です カフェラテは400円です キーだけを処置したいとき ● each_key each_keyメソッドはキーだけで繰り返し行うメソッドです。 menu = {"コーヒ" => 300,"カフェラテ" => 400} menu.each_key do |key| puts key end ↪️コーヒ   カフェラテ 同様に値だけを繰り返しするeach_valueメソッドもあります
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SSMを使ってEC2内のシェルスクリプトを動かす

lamdaから、rubySDKを使いec2内にあるshスクリプトファイルを実行したい。 準備 lamdaにポリシーをアタッチ ①AmazonSSMFullAccess ②AmazonEC2RoleforSSM を与えた。 ざっくり与えすぎてるのでも精査する必要があり。 参考にしないで下さい。。 EC2の準備 最近のversionのec2は最初からSSMエージェントがインストールされているらしいが、 古いEC2などの場合は手動でインストールする必要がある。 東京リージョンのAmazon Linux (x86_64)の場合 ① EC2にエージェントをインストール sudo yum install -y https://s3.ap-northeast-1.amazonaws.com/amazon-ssm-ap-northeast-1/latest/linux_amd64/amazon-ssm-agent.rpm 参考 https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/agent-install-al.html ②EC2にポリシーをアタッチ EC2にくっついてるロールに「AmazonSSMManagedInstanceCore」ポリシーをアタッチする。 実装 以下のようになる。 ssm_client = Aws::SSM::Client.new( region: 'ap-northeast-1') command = 'sh /home/test.sh' resp = ssm_client.send_command({ instance_ids: ["i-06efd939bf8b0fef7"], document_name: "AWS-RunShellScript", timeout_seconds: 3600, comment: "Comment", parameters: { "commands" => [command]} }) send_commandの使い方は以下を参考 https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/SSM/Client.html#send_command-instance_method
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

lamdaからEC2でコマンドを実行[AWS Systems Manager�]

lamdaから、rubySDKを使いec2内にあるshスクリプトファイルを実行したい。 AWS Systems Manager (旧称 SSM)を使う。 準備 lamdaにポリシーをアタッチ ①AmazonSSMFullAccess ②AmazonEC2RoleforSSM を与えた。 ざっくり与えすぎてるのでも精査する必要があり。 参考にしないで下さい。。 EC2の準備 最近のversionのec2は最初からSSMエージェントがインストールされているらしいが、 古いEC2などの場合は手動でインストールする必要がある。 東京リージョンのAmazon Linux (x86_64)の場合 ① EC2にエージェントをインストール sudo yum install -y https://s3.ap-northeast-1.amazonaws.com/amazon-ssm-ap-northeast-1/latest/linux_amd64/amazon-ssm-agent.rpm 参考 https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/agent-install-al.html ②EC2にポリシーをアタッチ EC2にくっついてるロールに「AmazonSSMManagedInstanceCore」ポリシーをアタッチする。 実装 以下のようになる。 ssm_client = Aws::SSM::Client.new( region: 'ap-northeast-1') command = 'sh /home/test.sh' resp = ssm_client.send_command({ instance_ids: ["i-06efd939bf8b0fef7"], document_name: "AWS-RunShellScript", timeout_seconds: 3600, comment: "Comment", parameters: { "commands" => [command]} }) send_commandの使い方は以下を参考 https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/SSM/Client.html#send_command-instance_method
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pathの引数について

Rspecでテストを書いているとき、下記の実装に関してpathの引数にtask.projectが使われていることに疑問を感じておりました。 describe 'Task詳細' do context '正常系' do it 'Taskが表示されること' do visit project_task_path(task.project, task) #project_task_path(project, task) ではだめなの? expect(page).to have_content(task.title) expect(page).to have_content(task.status) expect(page).to have_content(task.deadline.strftime('%Y-%m-%d %H:%M')) expect(current_path).to eq project_task_path(task.project, task) end end end 前提条件 taskとprojectがassociationの関係を持つ。 class project < ApplicationRecord has_many tasks end class Task < ApplicationRecord belongs_to :project end 考え方 まず考えるべきなのが、どのようなURLが生成されるべきかということ。 ここではTaskの詳細画面なのでURL/project/id/task/id/show となるべきですね。 次にtaskとprojectがassociationの関係を持つということ。 class project < ApplicationRecord has_many tasks end class Task < ApplicationRecord belongs_to :project end これによりtaskはproject(親)のidを持つことになります。 実際にpathの引数のレコードを調べるとこんな感じ。 (byebug) project #<**Project id: 1**, name: "dolorum", status: "todo", release_date: "2019-05-24", created_at: "2021-06-28 14:23:59", updated_at: "2021-06-28 14:23:59"> (byebug) task #<Task id: 3, title: "Task", status: "done", deadline: "2019-10-17 15:00:00", completion_date: nil, description: nil, **project_id: 2**, created_at: "2021-06-22 10:38:01", updated_at: "2021-06-22 10:38:01"> (byebug) task.project #<**Project id: 2**, name: "repudiandae", status: "todo", release_date: "2019-11-05", created_at: "2021-06-22 10:38:01", updated_at: "2021-06-22 10:38:01"> 注目すべきはproject_id ****の値。 project_id: 1に対しtaskはproject_id: 2を持っていますね。 これはassociationの関係とは言えません。 実際project_task_path(project, task)でもURL自体は生成されますが、これではproject_idが異なるため、生成されるURLのidはassociationの関係ではなくなってしまいます。 project_task_path(project, task) => /project/1/task/3 project_task_path(task.project, task)のようにしてproject(親)のidをtask.projectの返り値から取得することで、生成されるURLはassociationの関係を保つことができます。 project_task_path(task.project, task) => /project/2/task/3 引数の順番を入れ替てみる 以下のような結果から、pathの引数はassociationで定義したことなど関係なく、引数に指定した順番の通りにidを渡すようです。 project_task_path(task.project, task) => /project/2/task/3 # 引数の順番を入れ替えた結果 project_task_path(task, task.project) => /project/3/task/2 なので、引数を指定する際は定義したassociationの関係や、実際のブラウザの挙動を意識して、引数を指定してあげる必要があります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】N+1問題についてのまとめ

N+1問題とは? N+1問題とは、データベースからデータを取り出す際に、大量のSQLが発行されてパフォーマンスが低下してしまう問題のことです。 N+1問題の具体例 railsではallメソッドやfindメソッドを使ってデータベースからデータを取得しています。 ターミナルのログを見ると実際には下のようにその都度SQLが実行されています。 SQL Product Load (2.7ms) SELECT `products`.* FROM `products` usersテーブル id name 1 山田 2 新井 3 田中 4 北川 productsテーブル id product group_id 1 カレー 2 2 魚 1 3 焼肉 3 4 刺身  1 「1人のuserは複数のproductsを持つ関係なので、Userモデルにhas_manyメソッドを定義し、Productモデルにはbelongs_toメソッドを定義します。 UserモデルとProductモデルにアソシエーション定義 UserモデルとProductモデルにアソシエーション定義 # User.rb class User < ActiveRecord::Base has_many :Products end # Product.rb class Product < ActiveRecord::Base belongs_to :User end 全ての所有の商品一覧」をviewで表示したい場合に、controller側で全てのuserをallメソッドで取得し、view側で飼い主の持つproductsをアソシエーションによって下記の様に記述する事が出来ます。 N+1問題が起きてしまうコードを確認 # controller @users = User.all # view @users.each do |user| user.products.each do |product| product.name end end 「User.all」のコードが実行されると、「usersテーブルからusersテーブルの全てのカラム」が取得。 このSQLによって、usersテーブルに1回のアクセス。 次にveiw側。SQLをみると、productsテーブルに対して、4回のアクセスが行われている。 N+1問題が起きてしまうコード @groups.each do |group| group.products.each do |product| cat.name end end # このコードが4回のSQL文を発行 SELECT `products`.* FROM `products` WHERE `products`.`group_id` = 1 SELECT `products`.* FROM `products` WHERE `products`.`group_id` = 2 SELECT `products`.* FROM `products` WHERE `products`.`group_id` = 3 SELECT `products`.* FROM `products` WHERE `products`.`group_id` = 4 eachメソッドで@groupsが持つusersテーブルから全てのレコードを一つずつgroupに入れている。 SQLが「usersテーブルへのアクセスが1回 」に対して「productsテーブルへのアクセスがgroupsテーブルのレコードの数(4回)」発行 このようにアクセス1回に対して、関連するテーブルがN回発行されている1+Nの状況を「N+1問題」と言う。 。 N+1問題の対処法 includesメソッド includesメソッド includesメソッドの使用例 --> @users = User.includes(:user) @users = User.allで取得していた箇所を@users = User.includes(:products)に変更します includesメソッド @users = User.includes(:products) # User.allから変更 # 発行される2つのSQL SELECT `users`.* FROM `users` SELECT `products`.* FROM `products` WHERE `products`.`group_id` IN (1, 2, 3, 4) 2つのSQLが発行されました。1行目は、usersテーブルの全てのレコードを取得するSQL文です。 2行目は、productsテーブルからWHERE句で指定した条件にマッチするレコードを取得しています。 まとめ includesメソッドを使わない場合は、関連するuser_idカラムの値を1つずつ指定して取得していたのでproductsテーブルに4回のアクセスが必要でしたが、IN句でカラムの値をまとめて指定した事によって1回で取得出来るようになりました。includesメソッドを使ってレコードをまとめて取得させる事によって必要以上のSQLを発行する事なく済み,パフォーマンス向上に繋がる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【第13章】Railsチュートリアルでheroku pg:reset DATABASEを実行時に警告がでる

Railsチュートリアル第13章(第6版)で、herokuでレコードを削除する際に手間取ったので備忘として記録する。 ①heroku pg:reset DATABASEを実行すると警告がでる チュートリアル終了時にherokuのDBをリセットしようとすると、Warning: Cannot open browserが表示されて実行されない。 ②解決方法 heloku login --interactiveを実行すると、正常にログインできる。 参照:https://qiita.com/yadon/items/db3ec786ec410990cec6 ③再度heroku pg:reset DATABASEを実行 表示されるメッセージ通りに入力すれば、成功する。 参照:https://qiita.com/kakiuchis/items/5597354b1901c1371c9e
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails + React 画像読み込み方法

手順 画像ファイルを app/assets/images へ配置 config/webpacker.yml に追記 jsx ファイルに Component を作成 1. 画像ファイルを app/assets/images へ配置 適当な画像ファイルをapp/assets/imagesへ配置します。 2. config/webpacker.yml に追記 config/webpacker.yml # Additional paths webpack should lookup modules # ['app/assets', 'engine/foo/app/assets'] # resolved_pathsを下記のように変更 resolved_paths: ["app/assets/images"] 3. jsx ファイルに Component を作成 test.jsx import Image from 'test.png'; export default function TestComponent(props) { // ImageComponent 作成 const ImageComponent = p => <img src={Image} /> return ( // ImageComponent 読み込み <ImageComponent></ImageComponent> ); } 参考 Rails × React で画像を読み込む方法 https://forestbook-freelance.com/2021/04/12/rails-x-react-%E3%81%A7%E7%94%BB%E5%83%8F%E3%82%92%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%82%80%E6%96%B9%E6%B3%95/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RubyKaigi Takeout 2021参加レポート

こんにちは。今回はRubyKaigi Takeout 2021に参加してきました。 昨年に引き続きオンラインでの開催ということで、自宅からの参加です。オンラインでの開催はコメント欄などで反応がリアルタイムに見れるのが楽しいですね。 参加したいくつかのセッションをご紹介します。 一日目 Keynote: TypeProf for IDE: Enrich Dev-Experience without Annotations Ruby 3.1にbundleされるTypeProfの話からRubyKaigi Takeout 2021はスタート。 TypeProfによって静的型付けでの開発がより快適に進められる様子を見ることができました。 型推論がエディタにリアルタイムで表示される様におおっとなりました。 今後の課題としては、IDEでの動作に時間がかかるとのことでしたが、 RBSを書くことによってスピードアップが見込まれるため、書くとよさそうという話もありました。 RuboCop in 2021: Stable and Beyond Rubocopの1.0が昨年リリースになったものの、1.0以前のDL数が圧倒的に多いということで、 アップグレードする後押しとして、1.0の特徴が紹介されました。 1.0 以降はアップグレード時に互換性を担保しており、アップグレードしたことによる急なrubocopエラーの上昇にはならなそうです。 また、--parallel オプションがデフォルトとなり、環境によっては約6倍の速さになるとのこと。 Parallel testing with Ractors: putting CPUs to work Ractorを利用した並列テストの実行について。 複数CPUを活用するときに、予めどのテストがどのCPUを利用するか決まっている場合、適切にテストを配分できないことが想定されますが、テストの進行に合わせてCPUに配分できれば、最適化できそうです。 複数CPUを活用するなら、Ractorが使える?ということで、テストを並列化し、それを実際に実行してみせてくれました。 二日目 The newsletter of RBS updates Ruby3.1におけるRBSの更新について。 依存ライブラリの管理をするためのrbs collectionや、bounded type parameter, Generic Type Aliasの導入が発表されました。 RBSを使うにあたっての柔軟性が上がって、より使いやすくなりそうという印象を受けました。 include/prepend in refinements should be prohibited refinementsの機能の修正方針について。 refineはモンキーパッチを当てるのに便利な機能ですが、この中でmoduleをincludeすると不具合が発生することがあるため、 include/prependを禁止し、代わりにimportを導入するとのこと。 Graphical Terminal User Interface of Ruby 3.1 めちゃくちゃ自然がキレイでした、スイカ美味しそう ダイアログウィンドウつかってみたいです。 Ruby Committers vs the World これからのRubyをどうしていくか?について、コミッターの話が直に聞ける機会。 新機能の検討から開発環境まで、Ruby開発の裏側を知ることができました。 三日目 How to develop the Standard Libraries of Ruby? Ruby3.0以降でStandard Libraryをgem化して、各機能の導入をカスタムできるようにする流れについて。 各機能における問題をRubyの本体の問題と切り離すことができるメリットに頷かされました。 Ruby, Ractor, QUIC 高速な通信を目指して開発された新しい通信プロトコルQUICにRactorを利用して対応してみようという取り組み。 QUICの説明からパケットをどうRubyで扱うかが丁寧に説明されていて面白かったです。 残念ながらRactor内でOpenSSLが使えずに終わってしまったものの、Ractorの使える幅が今後もっと広がればいろんなところに可能性があるなと改めて思いました。 10 years of Ruby-powered citizen science 私はこれまで存じ上げなかったのですが、Safecastという福島の原発事故を発端とした、有用なデータを収集・公開するプロジェクトについての紹介でした。 バックグラウンドにRubyが使われており、そのこと自体の関心はもちろん、収集した情報のAPIでの提供や、grafanaを利用したグラフが公開されているなど、一般の人が収集されたデータに触れやすいようになっている取り組みが素晴らしいと思いました。 Matz Keynote Ruby3.0で追加された各機能の振り返りと、その使い勝手を更によくするような今回の3.1のアップデートについて。 全体を通して、Rubyを利用した開発体験が更によくなっていきそうという印象を受けました。 終わり 個人的には3.0で追加されたRactorが興味深くて、もっといろんなgemが対応するようになると嬉しいなと思いました。 また、静的型付けを利用した開発環境がかなり整っていく様が伝わってきて、この開発体験を確かに利用してみたい!という気持ちが増しました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

masterブランチで作業してしまった時の対処法

<手順> 1, まずは修正内容を確認する。 $ git status #ターミナルに作業した内容、変更した内容が出てくる 2, 修正内容を別の場所に一時保管する。 $ git stash その後もう一度git statusをすると先ほどの修正内容はなくなり、ターミナルに下記のような文章が出るはず。 On branch master Your branch is up to date with 'origin/master'. nothing to commit, working tree clean 3, 本来作業をしたかったブランチに切り替えorブランチ作り切り替える。 git checkout -b ○○○ 4, 一時保管していた内容を切り替えたブランチに適用する。 git stash apply
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

プルリクエストでコンフリクトが発生した際の対処法

そもそもコンフリクトとは? 直訳すると「衝突」という意味。 ファイル上で「自分の作業」と「他人の作業」が重複してしまった状態のことを指す。   発生原因 「同一ファイル」の「同一箇所」を複数人が編集した状態でマージした時に発生する。 また、ローカルブランチをpushした後にmasterブランチに変更を加えてしまうことでも発生する。   git pull –rebase を使った対処法 <手順> 1, masterブランチを最新にして、作業ブランチをチェックアウト $ git checkout master $ git fetch origin $ git pull origin master $ git checkout [作業ブランチ] 2, 作業ブランチに対してmasterブランチをrebaseする $ git pull --rebase origin master 3, コンフリクトがあれば直す error: Failed to merge in the changes. #このようなエラーが発生したらコンフリクトなので対象ファイルを修正 #対象のファイルを開くと、コンフリクト起こした部分は以下のように表示される <<<<<<< HEAD [作業ブランチでの変更内容] ... ======= [マージしたブランチでの変更内容] ... >>>>>>> develop コンフリクトが発生しなくなるまで上記手順を繰り返す。   git pull origin master実行時にコンフリクトした時の対処法 <原因> ローカルブランチをpushした後にmasterブランチに変更を加えてしまうことで、origin/master(リモートリポジトリ)とmaster(ローカルリポジトリ)の状態に差異が生じてしまっていること。 <対処法> 現在のブランチの状態を「強制的に」他のブランチの状態に合わせるときの方法として、git reset --hard を利用する。 $ git reset --hard origin/master # 現在のブランチの状態をorigin/masterに合わせる ⚠️ git reset --hardを実行すると手元にある作業ツリーとインデックスの変更内容は、すべてふっとんで消えてなくなるので、バックアップを取るなどして実行前は注意する必要がある。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

保守運用の際に既存メソッドの定義場所を調べるメソッド[Ruby, Rails]

想定 既存のアプリの改修を担当することになった 独自に定義されているメソッドについて、どこで定義されているか知りたい 結論 メソッドがどこで定義されているか調べるメソッド method("メソッド名").source_location #=> ["/Users/user_name/dir1/dir2/app/helpers/application_helper.rb", 78] # これが定義場所 使い方 このメソッドは、binding.pryを用いたデバッグ中やrailsコンソール時などに用いることが可能です。(恐らく 以下では、binding.pryを用いたデバッグを用いて、メソッドの定義場所を調査しています app/views/layouts/application.html.erb <!DOCTYPE html> <html> <head> : </head> <body> <%= flash_messages %> <% binding.pry %> <%= yield %> <%= javascript_include_tag 'application' %> </body> </html> サーバー内にて Frame number: 0/114 From: /Users/user_name/dir1/dir2/app/views/layouts/application.html.erb:209 207: <%= flash_messages %> 208: <% binding.pry %> => 209: <%= yield %> 210: <%= javascript_include_tag 'application' %> 211: [1] pry(#<#<Class:xxxxxxxxxxxxxx>>)> method("flash_messages").source_location => ["/Users/user_name/dir1/dir2/app/helpers/application_helper.rb", 78] 参考 さいごに 参考記事では、'x'.method(:blank?).source_locationでメソッドの定義場所を調査されていますが、自分の環境ではうまく動作しませんでした。 また、自分が紹介したmethod("メソッド名").source_locationでは、アプリ内で定義されているメソッド以外を調査することはできませんでした。(なんでだろう? 余談ですが… 久しぶりの投稿になります。現在インターンとして、実務開発に参加してから1カ月が経ちます。 初めての開発現場はとても学びが多いですが、全然アウトプットの時間を確保できていませんでした… これからは、自分の中でのアウトプットに対する敷居を下げ、誰に向けた記事なのか意識することで、少しずつアウトプットの時間を確保しようと思います! 最後までお読み頂き、ありがとうございました!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsのアクションについて

基本的事項の復習。 1. Railsにおける7つのアクション アクション 役割 HTTPメソッド index リソースの一覧を表示する get new リソースの新規作成 get create リソースを新規作成して保存する post show 記録されたリソースの詳細表示 get edit リソースの編集 get update リソースの更新 patch/post delete リソースの削除 delete 2. newとsaveの違い newのコマンドはリソースの生成のみを行うので、このままではDBに反映するにはsaveのコマンドの実行も必要になる。 createは生成して保存を行うため、イメージとしてはnew+saveというアクション。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

each_with_indexを使って要素の位置を知る

備忘録。 input = [1,3,5,6,9,12,23,55,78,90] 上記みたいな不規則な配列があったときに、特定の値の有無を返すプログラムを組みたいときにどうしたらいいか。 def search(target_num, input) 上記もメソッドと境内を使って、値があれば〜番目にあります、なければその数は含まれていない旨を返す。 ①inputに対してeach_with_indexを使う。 input.each_with_index do |num, index| これを使うと、input配列に対してnumはindex番目にあります、という情報が出力できるようになる。これ以下にif文を設けて条件を分岐させる。 ②どんなif文を書くか if num == target_num puts "#{index + 1}番目にあります" return end target_numは引数としてきているはずなので実体がある。これに対してinputの各要素(=num)に等しいものがあるかどうかを探すので一行目のような記述になる。 indexは0から始まるので+1して、実態に沿ってにわかりやすいやつにしておく。 ③elseは使わない。 eachを使っている都合上、ここでreturnを書かず、かつelseを使うと その数は含まれていません その数は含まれていません その数は含まれていません その数は含まれていません その数は含まれていません その数は含まれていません その数は含まれていません その数は含まれていません その数は含まれていません その数は含まれていません その数は含まれていません などと、ターミナルのメンタルがヘルスすることになるので、一旦returnで処理を終了させる。条件外の場合の処理を書くのはifの外。 後は search(15,input) などを記述して、値の検索をして帰ってくるものを確かめる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】PV数閲覧数機能(メモ)

やり方 まずGemfileにimpressionistを追記。 Gemfile gem 'impressionist' ターミナル $ bundle install 次にimpressionistテーブルを作成します。   ターミナル $ rails g impressionist マイグレーションファイルが生成されるのでrails db:migrateをします。 class CreateImpressionsTable < ActiveRecord::Migration[6.1] def self.up create_table :impressions, force: true do |t| t.string :impressionable_type t.integer :impressionable_id t.integer :user_id t.string :controller_name t.string :action_name t.string :view_name t.string :request_hash t.string :ip_address t.string :session_hash t.text :message t.text :referrer t.text :params t.timestamps end add_index :impressions, %i[impressionable_type message impressionable_id], name: 'impressionable_type_message_index', unique: false, length: { message: 255 } add_index :impressions, %i[impressionable_type impressionable_id request_hash], name: 'poly_request_index', unique: false add_index :impressions, %i[impressionable_type impressionable_id ip_address], name: 'poly_ip_index', unique: false add_index :impressions, %i[impressionable_type impressionable_id session_hash], name: 'poly_session_index', unique: false add_index :impressions, %i[controller_name action_name request_hash], name: 'controlleraction_request_index', unique: false add_index :impressions, %i[controller_name action_name ip_address], name: 'controlleraction_ip_index', unique: false add_index :impressions, %i[controller_name action_name session_hash], name: 'controlleraction_session_index', unique: false add_index :impressions, %i[impressionable_type impressionable_id params], name: 'poly_params_request_index', unique: false, length: { params: 255 } add_index :impressions, :user_id end def self.down drop_table :impressions end end ターミナル $ rails db:migrate 次にpostテーブルにカラムを追加する。 ターミナル $ rails g migration AddImpressionsCountToPosts impressions_count:integer マイグレーションファイルが生成されたら、defaurult: 0 を追記してrails db:migrateします。 class AddImpressionsCountToPosts < ActiveRecord::Migration[6.1] def change add_column :posts, :impressions_count, :integer, default: 0 end end ターミナル $ rails db:migrate postモデルにis_impressionable counter_cache: true追記していきます。 app/models/post.rb class Post < ApplicationRecord is_impressionable counter_cache: true postコントローラーに追記していきます。 aap/controllers/posts_controller.rb def show @post = Post.find(params[:id]) impressionist(@post, nil, unique: [:ip_address]) end 同じ人が何度か閲覧しても1PVのカウントになります。 ビューに記述。 app/views/show.html.erb <p><%= post.impressions_count %></p> これで閲覧数が表示されます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

reek での Repeated Conditional 警告への対処は継承か委譲を用いる

業務で主に Ruby On Rails での開発を行っていますが、そこで得た知見や、失敗への対応などについて記します。 今回は「reek から Repeated Conditional の警告を受けた」場合にどのように対処するか、についてです。 現状(要件) reek は Rubyプログラムの コードの臭い を検知するツールとして、弊社の開発でも活用しています。 その reek が発する警告の一つに Repeated Conditional というものがありますが、以下のように「一つのクラス内で、同じ条件判定を繰り返し定義している場合」に起こるものとされています。 # https://github.com/troessner/reek/blob/master/docs/Repeated-Conditional.md から引用 class RepeatedConditionals attr_accessor :switch def repeat_1 puts "Repeat 1!" if switch end def repeat_2 puts "Repeat 2!" if switch end def repeat_3 puts "Repeat 3!" if switch end end この警告の理由、および対策について同ページでは以下のように説明しています。 If you get this warning then you are probably not using the right abstraction or even more probable, missing an additional abstraction. (DeepL による翻訳) この警告が表示された場合は、正しい抽象化を使用していないか、さらには追加の抽象化を見逃している可能性があります。 ただ、これだけの説明だと「具体的にはどう解決すれば良いのか?」という疑問が生じます。 そこで、上記の例を「実際に解決する方法」を考えてみます。 1. まずは「抽象クラス」を切り出す 説明文には「抽象化を使用していない、または足りない」とあります。 「抽象化」の方法としてすぐに考えられるのは「継承を用いる方法」です。 上述の例の RepeatedConditionals クラスに、抽象クラスとして AbstractConditionals クラスを追加してみましょう。 # まずは単純に抽象クラスを追加します class AbstractConditionals attr_accessor :switch def repeat_1 raise NotImplementedError end def repeat_2 raise NotImplementedError end def repeat_3 raise NotImplementedError end end # 抽象クラスを継承した具象クラスとする class RepeatedConditionals < AbstractConditionals def repeat_1 puts "Repeat 1!" if switch end def repeat_2 puts "Repeat 2!" if switch end def repeat_3 puts "Repeat 3!" if switch end end 後の説明のために「この実装の使い方」も載せます conditionals = RepeatedConditionals.new conditionals.switch = true conditionals.repeat_1 conditionals.switch = false conditionals.repeat_2 まだこの時点では reek の Repeated Conditional 警告が出されたままです。 ではもう一歩、警告解消に向けて進むにはどうすれば良いでしょうか。 2. 具象クラスを(条件結果ごとに)分割する 警告解消のためには、これまでの説明通り「同じ条件判定を繰り返し定義している」のを解消すれば良いのですから、条件判定を「メソッド内」で行わずに「クラス単位」、すなわち「インスタンス生成時点」で行うようにしてみます。 そのためには switch が真の場合のクラスと、偽の場合のクラスに分ければ良いことになります。 # switch というアクセサ自体が無くなっています class AbstractConditionals def repeat_1 raise NotImplementedError end def repeat_2 raise NotImplementedError end def repeat_3 raise NotImplementedError end end # こちらは「swith が真だった場合の動作」を表すクラスです # switch が真であるという前提なので、 `if switch` が無くなっています class SwitchedOnConditionals < AbstractConditionals def repeat_1 puts "Repeat 1!" end def repeat_2 puts "Repeat 2!" end def repeat_3 puts "Repeat 3!" end end # こちらは「swith が偽だった場合の動作」を表すクラスです class SwitchedOffConditionals < AbstractConditionals def repeat_1; end def repeat_2; end def repeat_3; end end この時点で、reek の Repeated Conditional 警告は解消されます。 しかし、この対処の場合、インスタンスを生成する時点で「どちらのクラスを使うのか」を決定する必要が有るので、以下のような使い方になります。 switch = true conditionals = (switch ? SwitchedOnConditionals : SwitchedOffConditionals).new conditionals.repeat_1 switch = false conditionals = (switch ? SwitchedOnConditionals : SwitchedOffConditionals).new conditionals.repeat_2 これはベタに言えば「イケてない」感じがヒシヒシとします。 なぜなら、使用する側にとっては「どのクラスからインスタンスを生成したか」が関心事なのではなく、「switch の値に応じて正しい結果が返されるインスタンスである」ことだけが関心事だと考えられるからです。 3. どのクラスのインスタンスが生成されるかを隠蔽する とすれば、ここで思い出されるのが「GoFによるデザインパターン」の一つである Factory Method パターン です。 このパターンでは、何らかの方法で「具体的にどのクラスのインスタンスが生成されるのか」を隠蔽します。 今回は「生成メソッドの引数に switch 引数を与える」ことで、適切なクラスのインスタンスが生成されるようにします。 # 生成メソッドをクラスメソッドで定義するため、Abstract の prefix を取り止め、 # 実際に呼ばれることを目的としたクラス名とする class Conditionals class << self def build(switch) (switch ? SwitchedOnConditionals : SwitchedOffConditionals).new end end def repeat_1 raise NotImplementedError end def repeat_2 raise NotImplementedError end def repeat_3 raise NotImplementedError end end # 以下はこれまでと同じですが、後述のコードと比較しやすいように再度全体を載せます class SwitchedOnConditionals < Conditionals def repeat_1 puts "Repeat 1!" end def repeat_2 puts "Repeat 2!" end def repeat_3 puts "Repeat 3!" end end class SwitchedOffConditionals < Conditionals def repeat_1; end def repeat_2; end def repeat_3; end end 使い方は以下のようになります。 conditionals = Conditionals.build(true) conditionals.repeat_1 conditionals = Conditionals.build(false) conditionals.repeat_2 随分と簡単になりました。 ちょっと廻り道をしたような形になりましたが、しかし結果的にこれで reek の Repeated Conditional 警告を解消し、 かつ、使い方はこれまでとほぼ同じ(switch の値を使うのがインスタンス生成時になっただけ) という、ほぼ理想に近い状況になったことと思います。 4. (さらに考えて)インスタンス生成時に条件判定が出来ない場合は? これまでの記事では インスタンス生成時に条件判定をすることによって、メソッド実行時の条件判定を省ける ということを書いてきましたが、必ずしも「インスタンス生成時に条件判定が出来る」とは限らないと思います。 この記事の最初の例(Repeated Conditional 警告の説明ページの例)に立ち返って考えると、たとえば conditionals = Conditionals.new conditionals.switch = true conditionals.repeat_1 conditionals.switch = false conditionals.repeat_2 のように、インスタンスの生成後に条件判定が変わる場合が有ります。これでは「インスタンスの属するクラスを分ける」対処が出来ません。 これの対処として考えられることは「委譲を使う」ことになります。 すなわち、条件判定を要するメソッドを別クラスに分離して、switch の値が切り替わるごとに差し替えてしまうことです。 以下にコード例を示します。 class Conditionals attr_reader :switch def switch=(value) @repeater = value ? SwitchedOnRepeater.new : SwitchedOffRepeater.new @switch = value end # 実際は `activesupport` gem の delegate で実装する方が簡単です # ```ruby # delegate :repeat_1, to: :repeater # ``` # https://api.rubyonrails.org/classes/Module.html#method-i-delegate def repeat_1 repeater.repeat_1 end def repeat_2 repeater.repeat_2 end def repeat_3 repeater.repeat_3 end private attr_reader :repeater end # 以下は「親クラスが Conditionals になっていない」だけで、これまでの例と同じです class SwitchedOnRepeater def repeat_1 puts "Repeat 1!" end def repeat_2 puts "Repeat 2!" end def repeat_3 puts "Repeat 3!" end end class SwitchedOffRepeater def repeat_1; end def repeat_2; end def repeat_3; end end まとめ reek の Repeated Conditional 警告を解消するには 具象クラスを作成するなど「継承」を用いるのが良い あるいは、その部分を「委譲」するのが良い といったことを書きました。 他にも reek の警告には「説明を読んでも、どう対処したら良いのかすぐには分からない」ものがありますので、何か解決策が見つかれば、その都度、記していきたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】【Ruby】約数の総和を出すプログラム!!

 Pythonを学び始めたので、簡単なプログラムからひたすら作っています。今回は約数の総和を出すプログラムを作ってみました。 def culculate_divisor(num): divisor = [i for i in range(1,num + 1) if num % i == 0] return divisor def culculate_limited_divisor(num,divisor_range): limited_divisor = [i for i in range(1,divisor_range + 1) if num % i == 0] return limited_divisor num = int(input("約数の総和を出したい整数を入力してください:")) divisor_range = int(input("和を出したい約数の範囲を指定してください:")) result = culculate_divisor(num) result2 = culculate_limited_divisor(num,divisor_range) print("約数の数は" + str(len(result)) + "です") print("約数の総和は" + str(sum(result)) + "です") print((str(divisor_range) + "以下の約数の和は" + str(sum(result2)) + "です"))  このようなコードであり、出力結果は以下のようになります。 約数の総和を出したい整数を入力してください:12600 和を出したい約数の範囲を指定してください:5000 約数の数は72です 約数の総和は48360です 5000以下の約数の和は29460です  ただ約数の総和を出すだけでなく、12600の約数のうち、5000以下の約数の和を出すといったこともできるようにしています。  範囲の指定はそこまで重要な機能ではないと考えたため、範囲の指定をした関数と、範囲の指定をしていない関数を2つ作り、一方を削除しやすいようにしました。この方が見やすいかなとも思いました。  再利用性を真剣に考えるならもっと抽象化できそうです。 Rubyで作っていたプログラム  また以前にはRubyで同じプログラムを作っていました。 def divisor(num,range) num_div = (1..num).select{ |count| num % count == 0 } range_div = (1..range).select{ |count| num % count == 0 } puts "約数の数は#{num_div.length}です" puts "約数の総和は#{num_div.sum}です" puts "#{range}以下の約数の和は#{range_div.sum}です" end puts "約数の総和を出したい整数を入力してください" num = gets.to_i puts "和を出したい約数の範囲を指定してください" range = gets.to_i divisor(num,range)  ツッコミどころは感じつつも、ひとまずブログを書いてコードを見せることがモチベーションになり、コメントをいただくことが勉強になっています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【アルゴリズムとデータ構造】�螺旋本をPython & Rubyで説いてみた(第2章 & 第3章)

記事について 完全未経験状態から独学で勉強し、今年の4月から都内一部上場企業のwebエンジニアとして働くことになりました。 まだまだ初心者エンジニアなので間違いあっても温かい目で見守ってください。 背景 最近のマイブームはアルゴリズムとデータ構造の勉強です。 実務でRuby使っているのでRubyでプログラミングコンテスト攻略のためのアルゴリズムとデータ構造(通称:螺旋本)を解いてみました(螺旋本はすべてC++で解説してある)。また、最近合わせてpythonも勉強しているので、pythonのコードも載せています。 本当はruby一本でいい気がしているのですが、アルゴリズムとデータ構造関連の技術書やプロコン(at_coder等)の解説がもっぱらc++かpythonなので、今後のためにpythonをある程度はできるようになっていたいと思い、、、、。そしていずれか機械学習系も扱ってみたいしちょうどいっか、、、、。 第2章 プロコンのためのアルゴリズムとデータ構造 Maximum Profit O(n^2)のコード pythonの場合 n = int(input()) a = [int(input()) for _ in range(n)] max_num = (10 ** 9) * -1 for j in range(1, n): for i in range(0, j): max_num = max([a[j] - a[i], max_num]) print(max_num) rubyの場合 n = gets.chomp.to_i a = n.times.map {gets.chomp.to_i} max_num = (10 ** 9) * -1 (1..n-1).each do |j| (0..j-1).each do |i| max_num = [a[j]-a[i], max_num].max end end puts max_num ループの中にループがあるためn*n回の計算量。時間がかかる。 O(n)のコード pythonの場合 n = int(input()) a = [int(input()) for _ in range(n)] max_num = (10 ** 9) * -1 min_num = a[0] for i in range(1, n): max_num = max(max_num, a[i] - min_num) min_num = min(min_num, a[i]) print(max_num) rubyの場合 n = gets.chomp.to_i a = n.times.map {gets.chomp.to_i} max_num = (10 ** 9) * -1 min_num = a[0] (1..n-1).each do |i| max_num = [a[i] - min_num, max_num].max min_num = [a[i], min_num].min end puts max_num 最小値を求めることによってn回の計算量になる。 第3章 初頭的整列 Insertion Sort pythonの場合 n = int(input()) a = [*map(int, input().split())] for i in range(n): v = a[i] j = i - 1 while j >= 0 and a[j] > v: a[j+1] = a[j] j -= 1 a[j+1] = v print(*a) rubyの場合 n = gets.chomp.to_i a = gets.split(" ").map(&:to_i) (0..n-1).each do |i| v = a[i] j = i - 1 while j >= 0 and a[j] > v a[j+1] = a[j] j -= 1 end a[j+1] = v puts a.join(" ") end vの値を基準に、vとvより左側にある値を全部比較して並べ替えるソート。 pythonのアンパッキング便利。rubyはもう少しリファクタできそうだけど、螺旋本に沿ってやるとこんな感じ。 計算量はO[n^2]。配列の最初の状態で計算量がだいぶ変わる。 Bubble Sort pythonの場合 n = int(input()) a = [*map(int, input().split())] count = 0 flag = 1 while flag: flag = 0 for j in range(n-1, 0, -1): if a[j] < a[j-1]: a[j], a[j-1] = a[j-1], a[j] flag = 1 count += 1 print(*a) print(count) rubyの場合 n = gets.chomp.to_i a = gets.split(" ").map(&:to_i) count = 0 flag = true while flag flag = false (n-1).downto(1) do |j| if a[j] < a[j-1] t = a[j] a[j] = a[j-1] a[j-1] = t flag = true count += 1 end end end puts a.join(" "), count 隣同士の値を比較し、小さいものを左へ左へ。繰り返し処理が1回終わるごとに左から順番が確定する。 全部並べ替えが終わればif文に引っかからず、flagがfalseになりループ終了。 計算量はO[n^2]。隣同士の交換なので>= or <=にしない限りstable。 Selection Sort pythonの場合 n = int(input()) a = [*map(int, input().split())] count = 0 for i in range(n): min_j = i for j in range(i, n): if a[j] < a[min_j]: min_j = j a[i], a[min_j] = a[min_j], a[i] if min_j != i: count += 1 print(*a) print(count) rubyの場合 n = gets.chomp.to_i a = gets.split(' ').map(&:to_i) count = 0 (0..n-1).each do |i| min_j = i (i..n-1).each do |j| if a[j] < a[min_j] min_j = j end end t = a[min_j] a[min_j] = a[i] a[i] = t count += 1 if min_j != i end puts a.join(" "), count 左から順に場所を決めていく。未ソート部分で1番小さい数字を左に持っていくのを繰り返す。 計算量はO[n^2]。選択ソートは時に不安定なソートになる。 Stable Sort pythonの場合 n = int(input()) a = input().split() def bubble_sort(a, n): for i in range(n): for j in reversed(range(i+1, n)): if a[j][1] < a[j-1][1]: a[j-1], a[j] = a[j], a[j-1] def selection_sort(a, n): for i in range(n-1): j_min = a.index(min(a[i:], key=lambda a: a[1]), i) a[i], a[j_min] = a[j_min], a[i] a_bubble = a[:] bubble_sort(a_bubble, n) print(*a_bubble) print("Stable") selection_sort(a, n) print(*a) print("Stable" if a == a_bubble else "Not stable") rubyの場合 n = gets.chomp.to_i a = gets.split(' ') def bubble_sort(a, n) (0..n-1).each do |i| (n-1).downto(1) do |j| if a[j][1] < a[j-1][1] t = a[j] a[j] = a[j-1] a[j-1] = t end end end end def selection_sort(a, n) (0..n-1).each do |i| min_j = i (i..n-1).each do |j| if a[j][1] < a[min_j][1] min_j = j end end t = a[min_j] a[min_j] = a[i] a[i] = t end end a_bubble = a.dup bubble_sort(a_bubble, n) puts a_bubble.join(' '), 'Stable' selection_sort(a, n) puts a.join(' ') puts a == a_bubble ? "Stable" : "Not stable" bubbleとselectionのソートメソッドは前述のアルゴリズム。ただ今回は入力値が[H2,C3,....]を想定している。 bubbleソートは安定ソート。selectionソートは不安定な場合がある。今回はbubbleと同じ配列をソートして、同じ配列になった場合は安定ソート、違う配列担った場合は不安定ソートとしている。 shell_sort 後日追加予定 最後に 螺旋本は19章あるので毎週3~4章は進めねば、、、。 螺旋本に沿ってコーディングしてるとはいえ、rubyにしてもpythonにしてもちょっとゴリ押し感強いコードだなと自分で書きながら思いました。次回からはもうちょい美しく。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【アルゴリズムとデータ構造】�螺旋本をPython & Rubyで解いてみた(第2章 & 第3章)

記事について 完全未経験状態から独学で勉強し、今年の4月から都内一部上場企業のwebエンジニアとして働くことになりました。 まだまだ初心者エンジニアなので間違いあっても温かい目で見守ってください。 背景 最近のマイブームはアルゴリズムとデータ構造の勉強です。 実務でRuby使っているのでRubyでプログラミングコンテスト攻略のためのアルゴリズムとデータ構造(通称:螺旋本)を解いてみました(螺旋本はすべてC++で解説してある)。また、最近合わせてpythonも勉強しているので、pythonのコードも載せています。 本当はruby一本でいい気がしているのですが、アルゴリズムとデータ構造関連の技術書やプロコン(at_coder等)の解説がもっぱらc++かpythonなので、今後のためにpythonをある程度はできるようになっていたいと思い、、、、。そしていずれか機械学習系も扱ってみたいしちょうどいっか、、、、。 第2章 プロコンのためのアルゴリズムとデータ構造 Maximum Profit O(n^2)のコード pythonの場合 n = int(input()) a = [int(input()) for _ in range(n)] max_num = (10 ** 9) * -1 for j in range(1, n): for i in range(0, j): max_num = max([a[j] - a[i], max_num]) print(max_num) rubyの場合 n = gets.chomp.to_i a = n.times.map {gets.chomp.to_i} max_num = (10 ** 9) * -1 (1..n-1).each do |j| (0..j-1).each do |i| max_num = [a[j]-a[i], max_num].max end end puts max_num ループの中にループがあるためn*n回の計算量。時間がかかる。 O(n)のコード pythonの場合 n = int(input()) a = [int(input()) for _ in range(n)] max_num = (10 ** 9) * -1 min_num = a[0] for i in range(1, n): max_num = max(max_num, a[i] - min_num) min_num = min(min_num, a[i]) print(max_num) rubyの場合 n = gets.chomp.to_i a = n.times.map {gets.chomp.to_i} max_num = (10 ** 9) * -1 min_num = a[0] (1..n-1).each do |i| max_num = [a[i] - min_num, max_num].max min_num = [a[i], min_num].min end puts max_num 最小値を求めることによってn回の計算量になる。 第3章 初頭的整列 Insertion Sort pythonの場合 n = int(input()) a = [*map(int, input().split())] for i in range(n): v = a[i] j = i - 1 while j >= 0 and a[j] > v: a[j+1] = a[j] j -= 1 a[j+1] = v print(*a) rubyの場合 n = gets.chomp.to_i a = gets.split(" ").map(&:to_i) (0..n-1).each do |i| v = a[i] j = i - 1 while j >= 0 and a[j] > v a[j+1] = a[j] j -= 1 end a[j+1] = v puts a.join(" ") end vの値を基準に、vとvより左側にある値を全部比較して並べ替えるソート。 pythonのアンパッキング便利。rubyはもう少しリファクタできそうだけど、螺旋本に沿ってやるとこんな感じ。 計算量はO[n^2]。配列の最初の状態で計算量がだいぶ変わる。 Bubble Sort pythonの場合 n = int(input()) a = [*map(int, input().split())] count = 0 flag = 1 while flag: flag = 0 for j in range(n-1, 0, -1): if a[j] < a[j-1]: a[j], a[j-1] = a[j-1], a[j] flag = 1 count += 1 print(*a) print(count) rubyの場合 n = gets.chomp.to_i a = gets.split(" ").map(&:to_i) count = 0 flag = true while flag flag = false (n-1).downto(1) do |j| if a[j] < a[j-1] t = a[j] a[j] = a[j-1] a[j-1] = t flag = true count += 1 end end end puts a.join(" "), count 隣同士の値を比較し、小さいものを左へ左へ。繰り返し処理が1回終わるごとに左から順番が確定する。 全部並べ替えが終わればif文に引っかからず、flagがfalseになりループ終了。 計算量はO[n^2]。隣同士の交換なので>= or <=にしない限りstable。 Selection Sort pythonの場合 n = int(input()) a = [*map(int, input().split())] count = 0 for i in range(n): min_j = i for j in range(i, n): if a[j] < a[min_j]: min_j = j a[i], a[min_j] = a[min_j], a[i] if min_j != i: count += 1 print(*a) print(count) rubyの場合 n = gets.chomp.to_i a = gets.split(' ').map(&:to_i) count = 0 (0..n-1).each do |i| min_j = i (i..n-1).each do |j| if a[j] < a[min_j] min_j = j end end t = a[min_j] a[min_j] = a[i] a[i] = t count += 1 if min_j != i end puts a.join(" "), count 左から順に場所を決めていく。未ソート部分で1番小さい数字を左に持っていくのを繰り返す。 計算量はO[n^2]。選択ソートは時に不安定なソートになる。 Stable Sort pythonの場合 n = int(input()) a = input().split() def bubble_sort(a, n): for i in range(n): for j in reversed(range(i+1, n)): if a[j][1] < a[j-1][1]: a[j-1], a[j] = a[j], a[j-1] def selection_sort(a, n): for i in range(n-1): j_min = a.index(min(a[i:], key=lambda a: a[1]), i) a[i], a[j_min] = a[j_min], a[i] a_bubble = a[:] bubble_sort(a_bubble, n) print(*a_bubble) print("Stable") selection_sort(a, n) print(*a) print("Stable" if a == a_bubble else "Not stable") rubyの場合 n = gets.chomp.to_i a = gets.split(' ') def bubble_sort(a, n) (0..n-1).each do |i| (n-1).downto(1) do |j| if a[j][1] < a[j-1][1] t = a[j] a[j] = a[j-1] a[j-1] = t end end end end def selection_sort(a, n) (0..n-1).each do |i| min_j = i (i..n-1).each do |j| if a[j][1] < a[min_j][1] min_j = j end end t = a[min_j] a[min_j] = a[i] a[i] = t end end a_bubble = a.dup bubble_sort(a_bubble, n) puts a_bubble.join(' '), 'Stable' selection_sort(a, n) puts a.join(' ') puts a == a_bubble ? "Stable" : "Not stable" bubbleとselectionのソートメソッドは前述のアルゴリズム。ただ今回は入力値が[H2,C3,....]を想定している。 bubbleソートは安定ソート。selectionソートは不安定な場合がある。今回はbubbleと同じ配列をソートして、同じ配列になった場合は安定ソート、違う配列担った場合は不安定ソートとしている。 shell_sort 後日追加予定 最後に 螺旋本は19章あるので毎週3~4章は進めねば、、、。 螺旋本に沿ってコーディングしてるとはいえ、rubyにしてもpythonにしてもちょっとゴリ押し感強いコードだなと自分で書きながら思いました。次回からはもうちょい美しく。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsのカスタムバリデーション機能を使ってNGワード(不適切な内容)を防いでみる

NGワード機能を追加しようと考えていたところ、 あまり参考にできる記事が見つからなかったので、記事を書いてみました。 Qiitaで記事を書くのは初めてですので、拙い面もありますがご了承ください。 やりたいこと 不適切な言葉(下品な言葉など)を防ぎたい htmlタグやurl,連続した文字("あああああ"など)を防ぎたい やったこと 1. modelにカスタムバリデーションを定義する(非推奨) 2. カスタムバリデータクラスを作る(推奨) テーブル構成 テーブル名 カラム名 User name Micropost content Comment content 1,2共通の操作 ブラックリストの作成(好きなように定義してください。) config/blacklist.yml --- - 不適切な単語1 - 不適切な単語2 - 不適切な単語3 不適切な単語は.gitignoreに入れてgitに載せないようにしておきましょう。 .gitignore #以下を追加 /config/blacklist.yml 1. modelにカスタムバリデーションを定義する(非推奨) まず、CommentのcontentとMicropostのcontentにNGワード機能を付けたいと思い、以下のようにカスタムバリデーションを定義してみました。 app/models/application_record.rb class ApplicationRecord < ActiveRecord::Base self.abstract_class = true def content_cannot_contain_blacklist_words #blacklist.ymlから不適切な言葉を読み取る blacklist = YAML.load_file('./config/blacklist.yml') #contentが空でなく、contentの中にblacklistの用語が含まれているかを追加 if content.present? && blacklist.any?{ |word| content.include?(word) } errors.add(:contain_blacklist_words, ": 不適切な言葉は使用できません") # エラー時「contain_blacklist_words : 不適切な言葉は使用できません」と表示される。 end end def content_cannot_contain_invalid_regex # same_character_regex: 連続する五文字以上の語("あああああ")などを防ぐ # url_regex: https(http)から始まるurlを防ぐ # html_regex: htmlタグを防ぐ # 正規表現のエスケープのため %r!正規表現!で囲む invalid_regex = { same_character_regex: %r!(.)\1{4,}!, url_regex: %r!https?://[\w/:%#\$&\?\(\)~\.=\+\-]+!, html_regex: %r!<(".*?"|'.*?'|[^'"])*?>!} # invalid_regexをkey,valueとして取り出しvalue.match?(content)で正規表現と一致しているかを調べる。 if content.present? && invalid_regex.any?{|key,value| value.match?(content)} errors.add(:contain_invalid_regex, ": 使うことのできない文字列が含まれています") end end end Micropost.rbにvalidateを追加(validationsと間違えないように) app/models/micropost.rb class Micropost < ApplicationRecord # 先ほどapplicationで定義した関数を追加 validate :content_cannot_contain_blacklist_words validate :content_cannot_contain_invalid_regex end Comment.rbにもvalidateを追加する app/models/comment.rb class Comment < ApplicationRecord # 先ほどapplicationで定義した関数を追加 validate :content_cannot_contain_blacklist_words validate :content_cannot_contain_invalid_regex end このやりかたでの問題点 contentにしか適用できていない(カラム名が変わると適用できない。例えばnameにvalidationを適応したい場合、content.present?の部分をname.present?などに変えたコードを用意しなければならないので、同じコードを繰り返してしまう。) validate :関数名 を何度も書かなければならない。 そもそもどのカラムに適用してるのかがわかりにくい(validates :content, ~ のように書きたい) 2. カスタムバリデータクラスを作る(推奨) 1の問題点を解決しようとするとカスタムバリデータクラスを作ればいいことを発見する この通りにやってみる Validatorの定義 まずapp/validators/[validator名].rbを作る。 今回はinvalid_words_validator.rbとした。 app/validators/invalid_words_validator.rb # InvalidWordsValidatorの部分は先ほど付けた名前をキャメルケースにしたものを使う。 class InvalidWordsValidator < ActiveModel::EachValidator # record : テーブル名のこと(Micripostとか) # attribute : カラム名(contentとか) # value : フォームに入力された値("おはよう","foobar"とか) def validate_each(record, attribute, value) # blacklist.ymlから不適切な言葉を読み取る blacklist = YAML.load_file('./config/blacklist.yml') # valueが空でないか、blacklistに入力された値が含まれているかを判断 if value.present? && blacklist.any?{ |word| value.include?(word) } # contain_blacklist_words "内容~"と出力したくなかったので第二引数を''としている。 #(あまりいい方法ではないかもしれません。) # :contain_blacklist_wordsはja.ymlなどに定義しておくとよい。 record.errors.add(:contain_blacklist_words,'') end # same_character_regex: 連続する五文字以上の語("あああああ")などを防ぐ # url_regex: https(http)から始まるurlを防ぐ # html_regex: htmlタグを防ぐ # 正規表現のエスケープのため %r!正規表現!で囲む invalid_regex = { same_character_regex: %r!(.)\1{4,}!, url_regex: %r!https?://[\w/:%#\$&\?\(\)~\.=\+\-]+!, html_regex: %r!<(".*?"|'.*?'|[^'"])*?>!} # invalid_regexをinvalid_key,invalid_valueとして取り出しinvalid_value.match?(value)で正規表現と一致しているかを調べる。 if value.present? && invalid_regex.any?{|invalid_key,invalid_value| invalid_value.match?(value)} record.errors.add(:contain_invalid_regex, '') end end end 適用したいモデルに以下を定義する。 今回は例としてuser.rbのnameに定義してみる。 app/models/user.rb # validates :カラム名,Validator定義した名前: true validates :name,invalid_words: true 以上です。お疲れさまでした。 参考にしたサイト  - Railsガイド Active Record バリデーション  - Rails tips: カスタムバリデータクラスを作る(翻訳)  - Railsアプリでイタズラ投稿(NGワード)と格闘してみた話  - コメント機能等で特定のワード(悪口など)を制限することは可能ですか?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む