Ruby hashクラス 【 hash[hoge] += 数値 】の謎
hashの勉強をしていて、ん????なんだこれ??見た事あるけど、いまいち頭に入ってこない😩!!となったので、手を動かして理解まで進めてみた!
よくわからなかったのはこんなコード!
hash = Hash.new(0) array = "Mississippi".chars array.each do |j| hash[j] += 1 end p hash #=> {"M"=>1, "i"=>4, "s"=>4, "p"=>2}
こういったコードを見た時、戻り値の表示が腑に落ちず、なぜこのように返ってくるのかとても疑問でした。
問題は
hash[j] += 1
の部分!array.each で"Mississippi"の一字ずつ変数j
に入れ、hash[j] += 1
の処理をする・・・ん?わからない・・。どうしてhashの形で合計されて出力されるのか。
初心者だからか、sum += 1
なら見慣れているけど、hash[j] += 1
ということは
hash[j] = hash[j] + 1
という事。最初の"M"を入れるとhash[M] = hash[M] + 1
になる。この結果が{"M" => 1}
となる事が見慣れてないせいか、理解に苦しんだ。+= 数字
をする事で、なぜvalueが1になるのか。sは"Mississippi"の中に4回も現れ、結果として"s" => 4
と出力されているが、hash[:s] => 1 = hash[s] + 1
を繰り返して、加算されている。どうせならhash[j] +=> 1
こんな感じにしてくれると、記号でしっくりくる。でも、やっぱりわからないので、コンソールで色々といじってみたらなんとなく理解できた!
# まずはhash.new(0)でデフォルト値を設定!しないと計算ができない。 h = Hash.new(0) # 今のところ当然中身は何もなし p h #=> {} h.keys #=> [] h.values #=> [] # hのkeyをsに設定するとデフォルトの0がvalueとして入る h[:s] #=> 0 # ここでh[:s]に + 1をしてみる h[:s] += 1 => 1 # ↑増えました!!という事は h[:s] += 1 => 2 h[:s] += 1 => 3 h[:s] += 1 => 4 # ↑ちゃんと1ずつ増えます! ここでp hをすると p h => {:s => 4} となりました!👍
コードと出力の結果の間を細かく確かめながらいくと、どう処理が行われて結果として出力されているのかがわかりました!違和感のあるコードは分解していこうと肝に命じて一件落着😄
ブロックの失敗😅【Ruby】
最近課題をやっていると頻繁にブロックを使う!ただ幾分理解足らずで、ミスばかりなので過ちを備忘録として・・・
<ブロック>とは
Rubyのコードで書かれた処理のかたまりで、メソッドに引数として値を渡す
[1,2,3,4,5,6,7].each do |i| puts i end or 3.times do |i| x = i * 5 puts x end
こんな感じで書ける。ブロックは基本do ~ end
の中に行いたい処理を書き、引数として値を渡している。因みに{.......}
をdo ~ endの代わりに使う事もでき、基本的には1行で完結するコードに関しては{.......}
を使い、複数行にわたるコードはdo ~ end
を使用!
ただ参考書やwebサイトを見て、簡単なコードを書くだけだった僕はいざコードを各段階でミスばかり!
# ブロックの中で、合計を取る! ['hoge.rb','fuga.rb','baz.rb'].each do |f| st = File::Stat.new(f) b = st.blocks t = b.inject{|sum,n| sum + n} put t end
オブジェクトのクラスが違うのと、謎のコードで値を取れるわけもなく、長い事ハマりました。またブロック内外部で、同じ変数を使ってしまって、前の変数の値を書き換えてしまったり。
t = 23 [1,2,3,4,5].each do |i| t = i *3 puts t end puts t => 15
てんでだめですね!
ある程度自由に記述できるのがブロックではあるけど、わかんない事を書きすぎて書いている自分も理解できなくなって切るので金輪際気を付けます!
またdo ~ end
と{......}
では結合度も違い、エラーの原因になってしまいそうなので、気をつけたい!ブロックをdo…endで書くか{…}で書くかにより挙動が変わる例
他にもyield
やproc
とかもあるので、理解に達したらブログ書きます!
最近、習慣にしたい事。
今、通っているFjordBootCampでは様々な課題があって、Rubyの扱っていく中での実装手順メモ!!
どうしたらその機能を実装できるか!といきなり何かの課題をつくろうとしても作れません!
まず実装したい機能がライブラリにないかRubyドキュメントをチェック!
大体の機能は組み込みライブラリや標準ライブラリを探せば大体やりたい事が見つかる。ただ初心者の僕には読みづらさがあったり、例を探してみると英語のちょっと難しめのコードがあったり、エラーで表示されない事がある!
※現在、有志でドキュメントを作っている
図解で頭に入れる学習が好きな僕としては、Rubyの構造がわかるような図解がトップページにあったりすると捗ると試みるも、まだ理解していない事を図解できず・・・が、そんなの甘えですし、ただ読む量が足りていないだけと、知識の未定着が主な原因!頑張ります!
もし、ドキュメントから実装したいライブラリがあったり、irbもしくはpryでどう動くのか、どんな値や形式で返ってくるのかを確かめて動いたら実装! 慣れない僕はそのままQiitaや個人ブログを漁り始め、真似て打ち込みながら実行しているが、やはりドキュメントやオフィシャルサイト公式から理解し実装していく事が、Rubyを理解する近道という事なので、もっとRubyの本を読み、コードを書いて場数を踏みます!
なるべく個人ブログは参考程度に、まずはドキュメント!ドキュメントを制するものはRubyを制すと信じ、Rubyを楽しみます★
Rubyのアクセサメソッドについて
学習したはずのアクセサメソッド。
class Car attr_accessor :name ,:price def initialize(name,price) @name = name @price = price end end car = Car.new('Prius',250_000) car.name => 'Prius' car.price => 250_000
以上のコードを見て、バカな私は
def name @name = name end
のように定義してないじゃないか!!!!!!!!!!
何で使えるんだ?!の様に変な思考に入り、混迷を極めてしまった。もしかしたらmoduleクラスのnameメソッド
を使っているのかと調べてドキュメントを見ても、見当違いの事が書いてあった。悩んで解決せず、メンターの駒形さんに聞くと、「アクセサメソッドにはセッターとゲッターがあって、nameメソッドを定義していると一緒の事になる」と説明を頂き、クリアになると共に、cherry本でやったではないかと、自分のばかさにホトホト呆れるほかありませんでした。
もしかしたら同じ失敗をして時間を無駄にしようとしている人、そして自分の備忘録の為に書きました。もう忘れません!
因みにattr_accessor
はattr_reader
とattr_writer
を一緒にまとめたもので、
class Car attr_reader :name ,:price # 同義 # def name # @name #end # def price # @price #end attr_writer :name,:price #同義 #def name=(value) # @name = (value) #end #def price=(value) # @price = (value) # end def initialize(name,price) @name = name @price = price end end car = Car.new('Prius',250_000) car.name => 'Prius' car.price => 250_000 car.name = 'Crown' # イコールの両サイドにスペース! => 'Crown'
と書く事ができ、通常同義
の部分はdef initialize
などの下から定義するのですが、比較の為attr
の後に記述しました。
何かあればご指摘いただけると幸いです!
正規表現で注意すべき点!について
最近正規表現を学習中、初心者歓迎!手と目で覚える正規表現入門を元に学習し、少しずつ苦手意識が薄れてきました。
今回はこのサイトの最後の方に正規表現について注意すべき事
(_+|\w+)*a のように、+ や * が ( ) の中にも外にも出てくる正規表現は危険です。こういう正規表現は内部的な組み合わせの数が爆発的に増え、とんでもなく遅くなることが多い
と言及されていたので、少し調べてみました!
まずはこのような080-1234-5678
電話番号のマッチングを例とし、比較する正規表現は以下の2点
①\d{3}-\d{4}-\d{4} ②.*-.*-.*
この2つの正規表現の処理の様子を見て行きたいと思います。
プログラム初心者の僕は、なるべくシンプルで簡潔である方(例の為、変な正規表現)が良いと思い②番を選んでいました。ただShin x Blogや多くの方が、正規表現は長い方が良いとおっしゃっています。
その理由をregular expressions 101で見てみます。
まずは①番
右上に6ステップとマッチし、これが過程です
こんな感じで正規表現を左から綺麗にマッチングされています。
次に②番
右上に27ステップと4倍以上の処理がかかっていて、過程がこちら
ちょっと飛ばします
②は.*
に差し掛かった時点で全てにマッチしてしまったが為に-
でマッチするものがなくなってしまい、一個ずつ戻りながら、マッチングを繰り返します。このようなマッチング方法をバックトラッキングwiki
と呼ぶようです。ちょっと効率悪く時間もその分かかってしまいます。
もし、もっと長い正規表現を.*
を多様していくと、ブログトップの引用のようにサーバーに高負荷がかかるのが容易に想像できます。
今回はわかりやすい②番のような変な正規表現で比較してしまいましたが、今後正規表現を使う際は、なるべく長く、マッチングがすぐにできるようなものを意識する必要がありますね!
わからないまま進んだ、戻り値とnil。
戻り値とnilとは一体何?
参考書やWEBサイトでちらほら見かける戻り値
やnil
。わからないままだと気持ちが悪いのでまとめてみました。
戻り値とは
最後の式を実行した結果。全てのメソッドに戻り値は存在する!
nilとは
「全く何もない」ことを表すRubyの「値」
文字で表してもよくわかりませんが、以下の画像をまず見て下さい。
今の今まで二段に別れて結果が出力されている事の意味を理解していませんでした。
まず、赤い汚い文字(100均のペン)で「戻り値」と書きました!「10」と「nil」という結果が出ています!上にも書きましたが、最後の式を実行した結果が帰ってきています!「10」はわかりますが、なぜputs ' a'
の戻り値が「nil」なのか?!
答えがRubyの公式サイトにあり、
「puts」は「nil」を返すそうです。「a」はただ出力されただけのもの。素人が深入りしすぎて推測するより、そういう仕組みになっているんだと思った方が良さそう。 因みにメソッドで見てみると
①def calc(a,b) a + b end calc(5,5) => 10 ②def output puts 'a' end output a => nil
メソッド①では最後の式を実行した結果の「10」が結果で、メソッド②では上でもお話ししている通り「a」を出力しただけで、戻る値が元々決められている「nil」が返ります!
ただ、戻り値をreturn
で指定できる
def get_value 'あ' return 'い' 'う' end return "い"
まだまだわからない事は多そうです。何かあればご指摘いただけると幸いです。
Rubyの配列の基本を見やすく整理してみました!
電子書籍で勉強をしていると戻ったあり進んだり、紙の本でも何ページも捲らなきゃいけないのでパッと見で分かるように書いてみました!結局ググりそう。