- 投稿日:2022-01-31T23:53:27+09:00
「Everyday Rails - RSpecによるRailsテスト入門」のセットアップで詰まったことと対処法まとめ
「Everyday Rails - RSpecによるRailsテスト入門」 職場の先輩におすすめしていただいたので、買ってみました。 そこのセットアップでつまずいたことと対処法を自分なりにまとめてみました。 「Everyday Rails - RSpecによるRailsテスト入門」のセットアップの手順 # ソースコードのダウンロード git clone git@github.com:JunichiIto/everydayrails-rspec-jp-2022.git # ディレクトリの移動 cd everydayrails-rspec-jp-2022 # 使用する Ruby バージョンを指定(本書では3.1.0を推奨。下記コマンドは rbenv を使用する場合) rbenv local 3.1.0 # gem のインストール bundle install # データベースのセットアップ等 bin/setup # JavaScript パッケージのインストール yarn install # foreman gem のインストール gem install foreman # サーバーの起動 bin/dev 「Everyday Rails - RSpecによるRailsテスト入門」 にこのようなパートがあると思います。27p目(2022/1/31時点)。 git clone時のエラー # ソースコードのダウンロード git clone git@github.com:JunichiIto/everydayrails-rspec-jp-2022.git で、 git@github.com: Permission denied (publickey) のようなエラーが起こると思います。 以下の記事をまんまやったらできました。 ssh等のよけいな設定がめんどくさければ下記のコマンドでもクローンできるかと思います。 git clone https://github.com/JunichiIto/everydayrails-rspec-jp-2022.git bin/setup時のエラー bin/setup を実行すると ActiveSupport::MessageEncryptor::InvalidMessage (ActiveSupport::MessageEncryptor::InvalidMessage) というエラーがでるかもしれません。 このエラーはどんなエラーかという 詳しいことの説明は、チェリー本や、Everyday Rails - RSpecの著書である伊藤淳一さんの下記の記事におまかせしたいと思います。 https://qiita.com/jnchito/items/a73bc2838bfab5240675 bin/setup時のエラーの解決 credentials.yml.encを削除 万が一master.keyがあった場合それも削除 sudo EDITOR="vi" bin/rails credentials:edit を実行、このときmaster.keyが作られたことを確認。 sudo chmod 764 config/master.key を実行。権限系のコマンドは自分もよくわかっていないので自己責任でお願いします。 sudo bin/setup を実行したらデータベースが作られると思います。 ついでにいうと、 # サーバーの起動 bin/dev も # サーバーの起動 sudo bin/dev とやると立ち上がりました。 いちいちsudoコマンドを実行しなくてもいい方法があるかもしれませんが、一応この方法でセットアップが完了しました。 環境 macOS Monterey 12.1 2022/1/31日にbrew updateを実行 ruby 3.1.0p0 Rails 7.0.1
- 投稿日:2022-01-31T18:54:24+09:00
let と let! の違い(遅延評価)
はじめに request specを書いているときに、エラーぶつかりました。その原因がletとlet!の違いによるものでした。 当時は違いを理解できていなかったため、エラーにかかって向き合う時間ができてよかったと思っています。 今回得た学びをまとめます。 開発環境 ruby '3.0.0' Rails 6.0.3.4 エラー原因 コントローラのテストをrequest specで実行する際、以下のようなソースで、indexアクションを検証していました。 request.rb RSpec.describe "Gadjets", type: :request do let(:user) {FactoryBot.create(:user, :a)} let(:category) {FactoryBot.create(:category)} let(:gadjet) {FactoryBot.create(:gadjet, user_id: user.id, category_id: category.id)} describe "Action test" do context "index" do it "ログインユーザの場合。リクエストが成功すること" do sign_in user get gadjets_path expect(response.status).to eq(200) end it "ログインユーザの場合。gadjet_titleが表示されること" do sign_in user get gadjets_path expect(response.body).to include "#{gadjet.gadjet_title}" end it "ログインしていないユーザの場合、302レスポンスであること" do get gadjets_path expect(response.status).to eq(302) end end index.html.erb <% if @gadjets.present? %> <%# ログインしているユーザが投稿したgadjetがあるかを判定するフラグ%> <% @gadjets.each do |gadjet| %> **省略** <p class="card-text"><%= gadjet.gadjet_title.truncate(15) %></p **省略** <% end %> <% else %> <%= current_user.user_name %>が投稿したgadjetはありません <% end %> @gadjetsが存在すれば「gadjet_title」を表示し、存在しなければ「(ユーザ名)が投稿したgadjetはありません」と表示するものです。 ここのテストを実施した際に、結果は失敗でした。 it "ログインユーザの場合。gadjet_titleが表示されること" do sign_in user get gadjets_path expect(response.body).to include "#{gadjet.gadjet_title}" end エラー Failed examples: rspec ./spec/requests/gadjets_request.rb:17 # Gadjets Action test index ログインユーザの場合。gadjet_titleが表示されること <div class="col-md-4"> satoshiが投稿したgadjetはありません </div> どうやら”if @gadjets.present?”でelse(存在しない)を通っているみたいです。 しかし、request.rbにてletで:gadjetを定義し、インスタンスを作成しているのだが。。。 原因は遅延評価による特性のためでした。 let と let! の違い(遅延評価) エラー解決まで letで定義(遅延評価)すると、必要なときだけのみ評価されます。(letで定義した変数が参照されたタイミングで評価される)。 つまり必要ないときまで処理しないので余計な処理をせず結果的に、パフォーマンスが上がる。 それに比べlet!で定義するとテストを行う前(before doと同じタイミング)に評価されます。 また"it"ごとに変数が初期化されます。つまり無駄な初期化の時間が生まれる可能性があるため、無闇に使うのは控えたい。。 今回のケースで言えば、request.rbで "sign_in user"の記載でlet(:user) {FactoryBot.create(:user, :a)}が評価されることになります。 ":user"は参照されたため、評価されたのですが、肝心の":gadjet"は、参照されていないため、評価されていないことになります。 it "ログインユーザの場合。gadjet_titleが表示されること" do sign_in user #userを参照している get gadjets_path #index画面に遷移した時点で、":gadjetを参照していないため、”if @gadjets.present?”がelseになる expect(response.body).to include "#{gadjet.gadjet_title}" end そのため、let を let! に変更しテスト実行と同時に評価するよう変更したところ成功しました。 request.rb RSpec.describe "Gadjets", type: :request do let(:user) {FactoryBot.create(:user, :a)} let(:category) {FactoryBot.create(:category)} let!(:gadjet) {FactoryBot.create(:gadjet, user_id: user.id, category_id: category.id)} #!を追加 describe "Action test" do context "index" do it "ログインユーザの場合。リクエストが成功すること" do sign_in user get gadjets_path expect(response.status).to eq(200) end it "ログインユーザの場合。gadjet_titleが表示されること" do sign_in user get gadjets_path expect(response.body).to include "#{gadjet.gadjet_title}" end it "ログインしていないユーザの場合、302レスポンスであること" do get gadjets_path expect(response.status).to eq(302) end end 終わりに 個人的に、うやむやになっていた部分が今回エラーにかかったことで、理解が深まりました。 初学者にとって、遅延評価はつまづきやすいポイントだと思います。 少しでも参考になれば幸いです。
- 投稿日:2022-01-31T15:36:31+09:00
Rspecで特定のテストだけ実行する
特定のテストを実行 特定のテストを実行には2つあり、1つは実行したいテストのファイル名と行数を指定する方法。 ターミナル. $ rspec spec/models/user_spec.rb:8 2つ目はテストコードitの前にfをつけるだけでそのテストのみ実行される方法。 task_spec.rb require 'rails_helper' RSpec.describe Task, type: :model do describe 'validaton' do fit 'タスクを問題なく作成' do. #fitとなっているこのテストだけ実行される。 task = build(:task) expect(task).to be_valid expect(task.errors).to be_empty end it 'タイトル空欄は無効' do task = build(:task,title: "") expect(task).to be_invalid expect(task.errors[:title]).to eq ["can't be blank"] end end end ターミナルで入力するよりも、fitと入力したほうが簡単なので、これが使えるように設定していく。 spec_helperを編集 spec_helper.rb RSpec.configure do |config| config.filter_run_when_matching :focus #↑コメントアウトされているはずなので解除 end これで、fdescribe, fcontext, fit のように使うことができる。 参考記事 RSpecで特定のテストを実行する方法
- 投稿日:2022-01-31T14:13:41+09:00
[py2rb] 抽象基底クラス
はじめに 移植やってます。 ( from python 3.7 to ruby 2.7 ) 抽象基底クラス (Python) from collections.abc import Sized print(isinstance([1, 2], Sized)) print(issubclass(list, Sized)) print(isinstance([], Sized)) print(isinstance(set({1, 2}), Sized)) print(isinstance(3, Sized)) print(isinstance('Hello, world!', Sized)) print(isinstance({'id': 1, 'name': 'John'}, Sized)) # output True True True True False True True こちらの記事が分かりやすい。 defined? (Ruby) require 'set' def sizeable(obj) if obj.is_a?(Integer) false else begin obj.size true rescue => exception obj.method_defined? :size, true end end end puts sizeable(3) puts sizeable([]) puts sizeable([1, 2]) puts sizeable(Set.new([1, 2])) puts sizeable({'id': 1, 'name': 'John'}) puts sizeable('Hello, world!') puts sizeable(Array) puts sizeable(Class) # output false true true true true true true false Integer.sizeってバイト数を返すんですね。 花より団子で。 メモ Python の 抽象基底クラス を学習した 百里を行く者は九十里を半ばとす
- 投稿日:2022-01-31T10:29:24+09:00
ヒアドキュメントについて
rubyシルバーを勉強していくなかで、hashについてちゃんとわかっていなかった今回は解説させていただこうとおもいます。 ヒアドキュメントとは 複数行にわたる長い文章の文字列を扱い場合に便利な機能のことです。 言われてもピンとこないとおもいますので、実際にコードを見ていきましょう ヒアドキュメントを使わないときと、使うときで比較してみていきましょう puts "ヒアドキュメントを使わない場合はこのようにクオーテーションで囲い、\n改行したい箇所に\\nを書くことで改行できる。\nちなみにバックスラッシュを二つ使うことで改行(\\n)をエスケープできる" 上の書き方は、ヒアドキュメントを使わない書き方です。これでも、コード上は何も悪いわけではないのですが、なんか見た目悪くないですか? 可読性が落ちるんですね。 では次に、ヒアドキュメントを使う書き方を見ていきましょう。 text = <<EOS ヒアドキュメントを使わない場合はこのようにクオーテーションで囲い、 改行したい箇所に\\nを書くことで改行できる。 ちなみにバックスラッシュを二つ使うことで改行(\\n)をエスケープできる EOS puts text => #ヒアドキュメントを使わない場合はこのようにクオーテーションで囲い、 改行したい箇所に\nを書くことで改行できる。 ちなみにバックスラッシュを二つ使うことで改行(\n)をエスケープできる ヒアドキュメントを使うと、かなり見やすくなりましたね! ヒアドキュメントの使い方 今回では識別子に、EOSが使われていますが、EOSじゃなくてもいいんです。 慣習的に、EOS(End Of String), EOF(End Of File), EOL(End Of Line)が使われます。 でも、識別子が一致していれば、名前は何でもいいんです。 そのため、以下のような書き方でもOKです text = <<HOGE ヒアドキュメントを使わない場合はこのようにクオーテーションで囲い、 改行したい箇所に\\nを書くことで改行できる。 ちなみにバックスラッシュを二つ使うことで改行(\\n)をエスケープできる HOGE puts text => #ヒアドキュメントを使わない場合はこのようにクオーテーションで囲い、 改行したい箇所に\nを書くことで改行できる。 ちなみにバックスラッシュを二つ使うことで改行(\n)をエスケープできる インデントをつける場合 インデントをつけるときは、識別子の前に「-」をつけることでインデントがつくんです。 data = <<-EOF hoge fuga hogehoge EOF => " hoge\n fuga\n hogehoge\n" puts data # hoge fuga hogehoge インデントをつけない場合 インデントをつけないときは、識別子の前に「~」をつけることでインデントを排除できるんです data = <<~EOF hoge fuga hogehoge EOF => "hoge\nfuga\nhogehoge\n" 実際に問題を解いてみましょう ※選択されている回答は間違っています 今回の問題では、識別子の前に「-」をついています。つまりインデントが適用されるんですね。 よって、この問題の正解は " Hello,\n Ruby\n" が正解です 以上です。 何か間違いがございましたら、ご教示いただけますと幸いです。
- 投稿日:2022-01-31T10:26:58+09:00
RailsでJavaScriptファイルからのパスの通し方
実現したいこと RailsのJavaScriptファイル内に記載した、画像・音楽ファイルへのパスを正常に読み込ませたい✅ 開発環境 Ruby 3.0.2 Rails 6.1.4.4 結論 パスの指定を以下のように、asset_pathを使用する。 ※ JavaScriptの記載は、viewファイルのscriptタグ内に記載するか、hghg.js.erbのように、Rubyの埋め込みが可能な状態にする必要があります⚠️ 任意のファイル var ASSETS = { sound: { music: "<%= asset_path("energy.mp3") %>", ring: "<%= asset_path("tamborine.mp3") %>", }, json: { beatmap: "<%= asset_path("notes.json") %>" }, image: { 'bg': "<%= asset_path("club.jpg") %>" } }; manifest.jsにassets内の読み込むフォルダを指定する。 app/assets/config/manifest.js //= link_tree ../images //= link_tree ../music //= link_tree ../json これでOK!! エラー表示 間違ったパスの指定をしていた際のエラーも掲載しておきます? 下記コードのように、assets内の任意のファイルを相対パスで指定してもRouting Error(No route matches [GET] ... /assets...)になります。 任意のファイル var ASSETS = { sound: { music: "../../assets/music/energy.mp3", ring: "../../assets/music/tamborine.mp3", }, json: { beatmap: "../../assets/json/notes.json" }, image: { 'bg': "../../assets/images/club.jpg" } }; 最後に このエラーで数日詰まり、解決記事も他になかったので本記事を執筆しました✏️ 本記事が多くの悩める方に届けば幸いです!!!
- 投稿日:2022-01-31T01:36:12+09:00
Ruby で競技プログラミング (AtCoder) をやっているときあるある
はじめに 筆者はまだ競技プログラミング歴が浅い初心者である。Ruby が好きで、ふだん仕事でもプライベートでも Ruby を書く機会が多いため、Ruby で競技プログラミングをやっているのだが、競技プログラミングで書くコードはアプリケーションで書くコードとはかなり毛色が異なるため、アプリケーションの実装のノリで計算量が現実的ではないコードを書いてしまったり、Ruby っぽくない書き方をしてしまうことがある。 今回はそんな「Ruby で競技プログラミングをやっているときあるある」について紹介しようと思う。あまり役に立つような記事ではないため、娯楽程度に見てほしい。 用語 TLE Time Limit Exceeded (実行時間制限超過) の略 提出したコードが規定の処理時間をオーバーした際に表示される この場合、正解とはならず得点は得られない Ruby っぽくないループの書き方をしてしまう Ruby で配列のループを回すときは Array#each を用いるか、Array#map のような関数型言語のようなメソッドを用いることがほとんどだと思う。 そしてこの Array#each を使ったループの回し方は、他の言語にはあまりない1ユニークな手法なのではないかと個人的には思っている。他の言語では、for 文など、添字をインクリメントして添字で要素を指定するループの回し方が一般的だと思う。 ところが、競技プログラミングをやっている最中に限っては、トリッキーな位置の要素を指定することが多いため、Array#each のような、順番に要素のみを参照するメソッドではなく for 文や Integer#upto などを利用する頻度のほうが高かったりする。 たとえば、AtCoder Beginner Contest 237 の C 問題の解説 (C++) の 30 〜 35 行目2を見てみる。都合によりインデントを一段回上げている。 for (int i = x; i < (n - y); i++) { if (a[i] != a[x + n - y - i - 1]) { cout << "No" << endl; return 0; } } ループの開始が x で、終了が (n - y) というトリッキーな指定となっている。さらにその中の if の条件式で、a[x + n - y - i - 1] という、かなり複雑な添字の指定になっている。 これと同等のことを Array#each を使って表現するのはなかなか難しいだろう。もちろん添字を使うときのために Array#each_with_index というメソッドも存在するが、結局、添字しか使わないし、ループの開始と終了が 0 〜 N のように単純ではないので、ここでは筆者は以下のように Integer#upto を使いたくなるところだ。 x.upto(n - y - 1) do |i| if word[i] != word[x + n - y - i - 1] puts 'No' exit end end ちなみにこの項目を書くにあたって、改めて Ruby のループの書き方についておさらいしたのだが、Ruby にはこんなにループの書き方があるのかと驚かされた。 【Ruby入門】ループ処理まとめ(for・times・while・each・upto・downto・step・loop) 業務で使用するのはやはり専ら Array#each くらいだ。 Range#each で代用できてしまうことが判明 記事を書き終わったあとに RuboCop にかけてみて気づいたのだが、添字をレシーバにして Range#each を使用すれば意外にこれだけでもいけてしまうということがわかった。 たとえば、上記の Integer#upto を用いたコードを Range#each に書き換えると以下のようになる。 (x..(n - y - 1)).each do |i| if word[i] != word[x + n - y - i - 1] puts 'No' exit end end 他にも、for を使った以下のような書き方も Range#each が使えそうだ。 arr = %w[apple banana orange] n = arr.length # for for i in 0...n puts arr[i] end # Range#each (0...n).each do |i| puts arr[i] end for を Range#each に書き換えるのはまだ自然だが、Integer#upto を Range#each に書き換えるとカッコの数が増えて若干複雑になるから個人的には Integer#upto を使うかな。 計算量を気にせずに便利なメソッドを使ってしまう Ruby には (特に文字列や配列において) 他の言語にはないような便利なメソッドが数多くある。ループ内で多用しない限りは便利に使えるメソッドだったりするのだが、こと競技プログラミングにおいては計算量 (処理時間) が仇となって TLE になってしまうことが多発する。筆者も問題を解いているとき、かなりの頻度で TLE が発生するので、普段のコーディングで如何にパフォーマンスを軽んじているかを痛感してしまう。 たとえば、AtCoder Beginner Contest 237 の D 問題。与えられた文字列の文字種に応じて数値の挿入処理を行うというものだ。 配列の特定の位置に要素を挿入するのだから、Rubyist の素直な脳で考えると、Array#insert が使えそうだなとなる。実際に解いたコードがこちら。 #!/usr/bin/env ruby def main n = gets.to_i s = gets.chomp a = [0] pos = 0 for i in 0...n pos += 1 if s[i] == 'R' a.insert(pos, i + 1) end puts a.join(' ') end main これ以上なく簡潔に書けたのではないかと脳内で自画自賛しつつこのコードを提出したところ、TLE となった。原因は a.insert(pos, i + 1) の部分だ。 この Array#insert と同等の処理を行うメソッドを自作しようと思うとわかるのだが、指定した要素を挿入する位置よりも後ろにある要素の位置をすべて 1 つずつずらした上で挿入する必要があるので、それなりに時間がかかる。最悪の計算量は O(n) になると思う。それをループの中で何回も実行しているのだから、最悪の計算量は O(n^2) となり、これでは TLE になっても無理はない。 Array#insert のような便利メソッドがない言語なら、計算量的に時間がかかりすぎるということにすぐ気付けるため、そもそもこれと同等の処理を行うアルゴリズムを実装しようとは思わないが、Ruby だとこれがすぐに書けてしまうので、ついうっかり使ってしまうのである。 さて、一応、計算量的にも問題ないアルゴリズムも紹介する。解法はいくつか存在するようだが、上記のコードに近い実装の一つとして、2 つの空配列を用意して、挿入位置の左側と右側に分けてそれぞれ要素を追加していく方法がある。このアルゴリズムの詳細な解説は 公式サイト に上がっているのでこちらも参照してほしい。 このアルゴリズムで実装したコードがこちら。 #!/usr/bin/env ruby def main n = gets.to_i s = gets.chomp l = [] r = [] for i in 0...n if s[i] == 'L' r.unshift(i) else l.push(i) end end puts "#{l.join(' ')} #{n} #{r.join(' ')}".strip end main コード的には記述量が増えて最初に書いたコードほどスッキリはしていないが、こちらのコードであれば TLE にならずに解を求めることができる。 記述量が増えてスッキリしないと書いたが、それはそもそも Array#insert の中身を自分で書いていないからであって、もしこのメソッドのアルゴリズムも自前で書くことになったらきっとそのほうが複雑になるだろう。でも、そうとは感じさせないほど気軽に使えてしまうあたりが Ruby らしくもある。 また、この問題を解いたことにより、Array#insert は計算時間がかかるので、Array#unshift や Array#push が使えるならそちらを使おうという教訓が得られた。 便利なメソッドに頼りすぎて勉強にならない これは先ほどの「計算量を気にせずに便利なメソッドを使ってしまう」と対比している。計算量的にも問題なく、かつシンプルに書けてしまうのだが、その書き方では競技プログラミングをしている意味がない気がする……、というようなことがまあまあある。 たとえば、AtCoder Beginner Contest 237 の B 問題。与えられた行列の転置行列を求めるというものだ。 他の言語では 2 重ループで愚直に要素を入れ替えて転置行列を求める3が、Ruby だと Array#transpose というドンピシャなメソッドが存在する。これを使うと、以下のように書けてしまう。 #!/usr/bin/env ruby def main h, _w = gets.split.map(&:to_i) a = [] h.times do a << gets.split.map(&:to_i) end a = a.transpose a.each do |b| puts b.join(' ') end end main main メソッドの上のほうは入力値を変数に代入する処理で、下のほうは結果を出力する処理である。つまり、この問題の実質的なコードは a = a.transpose のたった 1 行のみとなってしまう。 このコードはもちろん正解だし、TLE にもならない。しかし、競技プログラミングをやって数学的な脳を養う、またはアルゴリズムを勉強するという観点でいうと本当にこれで良いんだろうか (いや良くない) という気持ちになる。 さいごに 今後も Ruby で競技プログラミングを続けていこうと思っているので、また何か気づいたあるあるがあれば加筆する。 あまりないと書いた直後、JavaScript の for ... of を思い出してしまったので、そうでもないのかな? とも思ってしまった。それでも、競技プログラミングで良く使われる C++ や Python などでは、添字をインクリメントして添字で要素を指定することが多い気がする。 ↩ 2022 年 1 月 31 日現在。 ↩ 他の言語にも転置行列を求めるメソッドやライブラリはあると思うが未調査。 ↩
- 投稿日:2022-01-31T00:54:44+09:00
ハッシュに関する解説
rubyシルバーを勉強していくなかで、hashについてちゃんとわかっていなかった今回は解説させていただこうとおもいます。 ハッシュって? ハッシュとは、配列と似ていますが、keyというものと、valueというものがある配列です。 具体的に、コードを見ていきましょう! hash = {'Satou' => 19, 'Hashimoto' => 35, 'Yamada' => 23} こういうふうな書き方をします。 keyというものが、Satou, Hashimoto, Yamadaです。 valueというものが、19, 35, 23です。 このハッシュのメリットが、keyを目印にして、valueを取得することができるんです。 これと似たものに配列というものがあります。 配列は、0,1,2のような数字で値を取得するような書き方をするんですね。 ハッシュの方が直感的でいいですね。 ハッシュの値の取り方 先ほどのコードを書いてみましょう hash = {'Satou' => 19, 'Hashimoto' => 35, 'Yamada' => 23} #keyを全部取得して、それを配列で返す。 hash.keys => ["Satou", "Hashimoto", "Yamada"] #valueを全部取得して、それを配列で返す。 hash.values => [19, 35, 23] #あるvalueに対応した、keyを取得 hash.key(19) => "Satou" ##あるkeyに対応した、valueを取得し、それを配列で返す。 hash.values_at('Satou') => [19] hash.['Satou'] => 19 そのほかの値の取得方法もありますので、気になる方は、こちらを参考にしてください! シンボルについて シンボルとは、「:」という書き方です。 では公式ドキュメントになんて書いてあるんでしょうか? Rubyの内部実装では、メソッド名や変数名、定数名、クラス名などの`名前'を整数で管理しています。これは名前を直接文字列として処理するよりも速度面で有利だからです。そしてその整数をRubyのコード上で表現したものがシンボルです。 シンボルは、ソース上では文字列のように見え、内部では整数として扱われる、両者を仲立ちするような存在です。 名前を管理するという役割上、シンボルと文字列は一対一に対応します。また、文字列と違い、immutable (変更不可)であり、同値ならば必ず同一です。 公式ドキュメントより つまり、見た目上は、文字列でも、中身は数字なんですね。 これの何がいいのか?僕個人的には、一番のメリットは処理速度が速いということだとおもいます。 他にメリットがありますので、それを知りたい方はこちらを参照してみてください。 実際のコードを見ていきましょう #書き方➀ hash = {:'Satou' => 19, :'Hashimoto' => 35, :'Yamada' => 23} puts hash[:'Satou'] # 19 #書き方➁ hash = {'Satou': 19, 'Hashimoto': 35, 'Yamada': 23} puts hash[:'Satou'] # 19 #値の取り方は、上のコードと同じようにとれる 実際に問題を解いてみよう この問題では、 klassという変数に、Classクラスから新しいインスタンスを作成して、そのオブジェクトを代入します。 そして、そのkeyであるオブジェクトに、valueの100を作ります。つまり、ハッシュをつくります。 3行目では、上のコードでも書いたような書き方で書かれていますね。 つまり、keyに紐づいた、valueを取得しています。 このコードを実行すると、取得できる値は、 klass = Class.new hash = {klass => 100} puts hash[klass] # 100 100が出てくるような選択肢を選べばいいんですね。 選択肢を見ていきましょう 選択肢1 klass = Class.new hash = {klass: 100} puts hash[klass] 1行目までは同じですね。 2行目もハッシュの書き方➁の書き方です。 3行目をよく見てみると、値の取り方が違いますね。もし、100を取りたかったら、 puts hash[:klass] と書くべきですね。 選択肢1の値の取り方をすると、nilになるんです。 選択肢2 klass = Class.new hash = {} hash.store(klass, 100) puts hash[klass] 2行目で、空のハッシュを作ります。 3行目でstoreメソッドを使ってます。storeとは英語で「格納する」という意味です。 つまり、hashにklassをkeyに、valueを100としたhashを作ります。コードで書くなら、 {#<Class:0x00000000086b8660>=>100} そして4行目で、keyに対応したvalueを取得してます。この書き方は、「ハッシュの値の取り方」の最後に書いてあります。 このコードを実行すると、100が取得できるので、1つ目の正解です 選択肢3 ここまで読んでいただいた方であれば、選択肢3はnilになることが分かるとおもいます。 選択肢1の解説を読んでいただければわかるとおもいます。 選択肢4 ここまで読んでいただいた方であれば、選択肢4は100になることが分かるとおもいます。 2行目のHashメソッドは、新しいハッシュを作成してくれるというメソッドです。 以上です。 何か間違いがございましたら、ご教示いただけますと幸いです。 【参考文献】