LoginSignup
1
1

Ruby Silver ポイントメモ

Last updated at Posted at 2024-03-19

はじめに

RubySilverに合格したので、試験対策メモと、実際の試験で間違えたと思われる箇所を共有します。個人的に要注意と思ったところのメモ書きなので、網羅はされておりません。Silver受験のお役に立てば幸いです!

ポイントメモ

演算子

  • 演算子の優先順位

    累乗(**)、四則演算子、&、|、比較演算子、&&、||、範囲演算子、条件演算子(?:)、=、and、or

    演算子式 (Ruby 3.3 リファレンスマニュアル)

  • 複数の三項演算子 a = 条件式1 ? 条件式2 ? "A" : "B" : "C";

    条件式1 条件式2 結果
    true true A
    true false B
    false true C
    false false C

条件式でのRangeオブジェクト範囲式

(2024.4.24追記:条件式として範囲式を用いる際はRangeオブジェクトは生成されない旨と、Rubyでの真偽とTrue/Falseの違いをコメントでご指摘いただき、訂正しました。
【参考URL】Ruby の真/偽と true/false は違う #Ruby - Qiita)

  • 「式1..式2」
    式1が真になったらtrue。そのとき式2も真なら1回trueの後初期状態へ。
    そのとき式2が偽なら、それ以降は式2が真になるまでtrueを返し、式2が真になったら初期状態へ。

  • 「式1…式2」
    基本同上だが、式1が真になったとき式2が真でも初期状態に戻らない。

10.times{|d| print d == 3..d == 5 ? "T" : "F" }
# => dは0~9まで。0~2はF、3~5はT、6~9はFなのでFFFTTTFFFFとなる。

オブジェクト指向、その他文法事項

  • irbで改行するには「\」を使う。メソッド名の前の「.」を付けてからの改行であればirbもOK。プログラム上は「.」が改行前後どちらでもエラーにならない。

  • メソッドと変数の探索順位は変数が先⇒同名のものがあれば変数が優先される

  • メソッド内で定数を宣言するとSyntaxError

  • メソッドの戻り値はreturnを記述しない場合は最後に記述した式の値が評価される

  • 定数の再代入は警告出るが、破壊的メソッドの呼び出しは警告出ない

    B = "foo"
    B.concat("bar")
    p B  #=> "foobar":警告なし
    
  • unlessはelse節は書けるがelsifは書けない!

  • do..endより{}のほうが結合度が高い。pで出力するときは()使うか{}で記述

    p [1,2,3,4].map do |e| e * e end  # => <Enumerator: [1, 2, 3, 4]:map>:mapまでがpされてる
    p [1,2,3,4].map { |e| e * e }  # => [1, 4, 9, 16]
    p ([1,2,3,4].map do |e| e * e end)  # => [1, 4, 9, 16]
    
  • &:<メソッド名>とするとSymbol#to_procが実行されます。

  • @@クラス変数は継承したクラスでも共有されます!!

  • 可変長引数は1つしか定義できない。最後でなくてもOK

  • 多重代入で右辺が不足している場合は、nilが代入される

  • メソッドと引数の間に空白あると解釈に注意

    foo (2) * 2 # => foo ((2) * 2)が呼ばれたと解釈される

  • 特異クラスと特異メソッドの定義

    特異クラス:指定したインスタンスだけに適用される特別なクラス。

    class << 対象のオブジェクト…(特異メソッド定義)…end

    特異メソッド:インスタンスに直接定義されたメソッド

    def <オブジェクト名>.<新たに定義するメソッド名>…end

    s = "Hello"  # Stringオブジェクト
    def s.greet  # オブジェクトsに特異メソッドを定義
        puts "Hi!"
    end
    class String
        def greet
            puts "Hello!"
        end
    end
    s.greet  # => "Hi!":クラスを拡張したメソッドより、特異メソッドが優先される
    
  • 継承クラスのsuperは引数も()も省略すると、現在の引数を引き継いで実行される

Object

  • Object#equal?:オブジェクトIDが同じかどうか比較。上書き不可
  • eql?は自作クラスでは上書きしないとオブジェクトID比較になってしまう

Array

  • Arrayの生成

    • Array(3)Array([3])と同じ:[3]が生成される。ブロックは無視される
    • Array.new(3){"a"}は要素数3の配列を作成し、ブロックの値でそれぞれ初期化
    • Array.new(3, "a")は要素数3の配列を作成し、全要素を"a"で初期化(同一オブジェクト)
  • select、filter:ブロックを評価し真になった要素を配列で返す、なければ空配列。非破壊

  • |和演算では重複要素は取り除かれる

  • flattenは平坦化した配列を返すが、flatten!は平坦化しなかった場合nilを返す

  • inject:引数に初期値を設定できる。しない場合は1,2番目の要素が最初に呼ばれる

    # 合計を計算する。
    p [2, 3, 4, 5].inject {|result, item| result + item }        #=> 14
    
    # 自乗和を計算する。初期値をセットする必要がある。
    p [2, 3, 4, 5].inject(0) {|result, item| result + item**2 }  #=> 54
    
  • 連番Arrayの用意[*1..10]

  • any?:真になる要素があれば即true。ブロックの戻り値がtrueになった時点で繰り返しを止める

  • delete_ifとreject!の違い:delete_ifは常にselfを返すが、reject!は削除しなかった場合nilを返す

  • partition:ブロックの評価が真だった要素と偽だった要素を2つの配列に入れて返す(二次元配列)

  • 多重代入

    # 要素を捨てる
    array = [1,2]
    x, = array
    p x #=> 1
    # **配列以外もあるときは、配列が1要素とみなされる**
    x, y = 1,[2, 3]
    p x  # => 1
    p y  # => [2,3]
    # **変数側に()があるときは配列と認識される**
    (x, y), z = 1, 2, 3 # y = nil, z = 2となり、3は無視される
    (x, y), z = [1, 2], 3 # y = 2, z = 3
    # 変数が一つであれば、配列として代入される
    a = 1,2,3  # => [1,2,3]
    
  • sortは<=>で比較できる必要があるので、文字列と数値が入った配列をsortするとArgumentErrorが発生する

  • push,append:末尾に追加

  • each_with_indexは引数取らないが、with_indexはoffsetを引数でとる

  • *”a”はArrayクラス hoge = *"a” #⇒[”a”]piyo = *7 #⇒[7]

  • zipとtranspose

    a = %w(a b c)
    b = [1,2,3]
    a.zip(b){|x|p x}  # ["a", 1]["b", 2]["c", 3]
    a.zip(b).each{|x|p x}  # 同上。zipした結果を出力している
    [a,b].transpose.each{|x,y|p [x,y]}  # 同上。transposeはレシーバに2次元以上かつ要素数共通の配列がないとエラーになる。
    [a,b].transpose.each{|x|p x}  # 同上。transposeの結果は2次元配列。要素数一つずつ出力するならパラメータ一つでよい
    [a,b].zip{|x,y| p [x,y]} # ["a", "b"] [1, 2]
    
    p [1,2,3].zip  #=> [[1], [2], [3]]:引数無しでzipを使うと、レシーバの要素ごとの配列が作られる
    p [[1,2,3],[4,5,6]].zip  #=> [[[1, 2, 3]], [[4, 5, 6]]]
    [[1,2,3],[4,5,6]].zip{|x| p x}  #=> [[1, 2, 3]]\n [[4, 5, 6]]  # ブロックでパラメータを1つにすると、配列が取り出される
    [[1,2,3],[4,5,6]].zip{|x,y| p [x,y]}  #=> [1, 2]\n [4, 5]  # パラメータを2つにすると、それぞれの配列の1つ目と2つ目の要素が取り出される
    [[1,2,3],[4,5,6]].zip{|x,y,z| p [x,y,z]}  #=> [1, 2, 3]\n [4, 5, 6]  # 同上、それぞれの配列から3要素ずつ取り出される
    

Hash

  • clear、replace:Arrayと共通のメソッド

  • invert:値とキーを入れ替えてハッシュを返す。キーが重なった場合は、あとの定義が優先される

  • Hash#sortの返り値はArray

  • store:キーと値を格納(hash[key] = valueでの格納と同じ) hash.store(key,value)

  • Hashの作り方:hash = {:a => 1, b: 3, "c" => 5}またはhash = Hash[:a, 1, :b, 3, :c, 5]

  • 空のHashオブジェクトを生成するにはHash({}){}Hash.new(デフォルト値),Hash[]のいずれかを用います。

  • Hashのデフォルト値

    デフォルト値が適用されたキーは、pなどでハッシュの内容を参照する際は表示されない

    Hash.new("foo")  # 同一オブジェクトのfooがデフォルトとなるので、一つの破壊的変更が他にも影響
    Hash.new{|hash,key| hash[key]="foo"} # 値がまだないキーが追加されるたびにブロックが評価されるので、異なるオブジェクト
    Hash.new{|hash,key| raise(IndexError,"hash[#{key}] has no value")} # 上記を利用して未登録のキーに対し例外を発生させる
    # Hash#default_proc=nilを指定すると現在のdefault_procをクリアする
    h = Hash.new([].freeze) # デフォルト値の変更が起きないようfreeze
    p h[0] #=>[]
    p h[0] += [0] #=>[0]:[] = [] + [0]:+は非破壊メソッドなのでエラー出ない。<<は破壊的メソッドなのでエラー発生
    
  • Hash#eachのブロックパラメータはArrayで渡される

    h = {a: 100}
    h.each {|p|
      p p  # => [:a, 100]:パラメータを2つ設定すると、キーと値それぞれが渡される
    	p p.class  # => Array
    }
    

String

  • delete:selfから引数に含まれる文字を除いた文字列を返す。非破壊。2つ以上の引数は&&判定。先頭に^がついていればそれらを除くstr.delete!("^2-41-") #⇒2,3,4,1,-を除く

  • tr、delete、squeezeは引数に正規表現は使えない

  • replaceは完全置き換えなので引数1つ。

  • chomp:rs が "\n" ($/ のデフォルト値) のときは、実行環境によらず "\r", "\r\n", "\n" のすべてを改行コードとみなして取り除く。末尾に改行コードがない時はなにもしない

  • chop:文字列の終端が "\r\n" であればその 2 文字を取り除く

  • chop,chomp,stripそれぞれ!(破壊的)か否か要注意!!!!

  • scan:self に対して pattern を繰り返しマッチし、マッチした部分文字列の配列を返す

    # 単語の出現回数をカウントする
    s = "To be or not to be, that is the question."
    hash = Hash.new(0)
    s.scan(/\w+/) {|i| hash[i] += 1} # +=の処置ならデフォルト値は変更されない
    p hash["be"] #=>2
    # ブロックを取る他の例
    "Ruby Examination".scan("xa") { |s| p s.upcase } # => "XA"
    
  • split:第2引数で分割戸数のlimitを指定できる。未指定時は0=制限なく分割

    また、正規表現内で()を使うと、デリミタを含んだ結果を返す

    ()だと\n,\tなどは削除、(//)だと\n,\tも含めた配列を返す

    p "a,b,c,d,e".split(/,/, 1)  # => ["a,b,c,d,e"]
    p "a,b,c,d,e".split(/,/, 2)  # => ["a", "b,c,d,e"]
    p "a,b,c,d,e".split(/,/, 3)  # => ["a", "b", "c,d,e"]
    p "a,b,c,d,e".split(/,/, 4)  # => ["a", "b", "c", "d,e"]
    p "a,b,c,d,e".split(/,/, 5)  # => ["a", "b", "c", "d", "e"]
    p "a,b,c,d,e".split(/,/, 6)  # => ["a", "b", "c", "d", "e"]
    p "a,b,c,d,e".split(/,/, 7)  # => ["a", "b", "c", "d", "e"]
    p "a,b,c,d,e".split /(,)/    # => ["a", ",", "b", ",", "c", ",", "d", ",", "e"]
    p "a b c d".split()          # => ["a", "b", "c", "d"]
    p "a\nb\nc\nd".split(//)     # => ["a", "\n", "b", "\n", "c", "\n", "d"]
    p "a\tb\tc\td".split()       # => ["a", "b", "c", "d"]
    p "a b c d".split(//)        # => ["a", " ", "b", " ", "c", " ", "d"]
    
  • splitで区切り文字を複数指定するときは正規表現を使う/; | :/

  • index(pattern, pos):pos番目から検索開始

  • ==、===、eql?:内容が同じか?

  • %演算⇒詰まった問題参照

  • Stringにも[]、sliceメソッドがあるよ

  • String#strip!は文字列の先頭と末尾の空白文字(\t\r\n\f\v)を取り除きます

  • to_i:できるとこまで整数にする。変換不可、空文字、nilには0を返す。引数で進数指定。引数0の場合は変換対象の接頭辞で判断

  • hex,octメソッドは変換できる文字が見つからなければ0を返す

  • Stringにできないこと

    • to_aメソッドはない!charsを利用
    • to_hメソッドもない!
    • appendはない!<<またはconcatを利用。ちなみに+は非破壊。

Integer

  • chr:引数で与えられたエンコーディングで数値を一文字に変換。正しく解釈できない場合はRangeError
  • ==、===:数値として等しいかどうか比較。1==1.0 #⇒true
  • eql?:同じクラスのオブジェクト同士で==で等しければtrue。1.eql?(1.0) #⇒false
  • equal?:オブジェクトIDが等しければtrue1.equal?1.0 #⇒false
  • Numeric#abs2:絶対値の2乗を返す
  • Numeric#step(limit, step)selfからstepずつ加算し、limitまでをブロックに渡します。
  • to_s:引数で進数の変更もできる

IO

  • read(テキスト名 , 読み込む文字数, offset = 開始位置):サイズ指定無ければEOFまで読み込む
  • write(ファイル名, 書き込む文字列, 開始位置)
  • seek(offset,whence):offsetの分だけポインタをwhenceから移動する。
    • IO::SEEK_SET:whenceのデフォルト。ファイルの先頭から
    • IO::SEEK_CUR:現在のポインタ位置から
    • IO::SEEK_END:ファイルの末尾から
メソッド                      空のファイルに対して
IO.read(空ファイル)           ""
IO.read(空ファイル, length)   nil
IO.readlines(空ファイル)      []
IO.foreach(空ファイル)        何もしない

メソッド                      既にEOFだったら
IO#each_byte                  何もしない
IO#getc                       nil
IO#gets                       nil
IO#read()                     ""
IO#read(length)               nil
IO#read_nonblock              EOFError
IO#readchar                   EOFError
IO#readline                   EOFError
IO#readlines                  []
IO#readpartial                EOFError
IO#sysread                    EOFError

File

  • chmod(モード、テキスト名)

  • chown

  • directory?:ファイルがディレクトリかどうかboolで返す

  • delete(テキスト名):削除成功すれば削除数、失敗した場合はエラーを返す

  • extname:ファイル名の拡張子部分を返す

  • basename:引数のfilenameの一番最後の/に続く文字列を返す。suffix指定すれば取り除く

  • dirname:引数に指定した文字列の一番後ろの/より前の文字列を返す

    File.dirname("text.txt") # => "."
    File.dirname("REx/text.txt") # => "REx"
    File.dirname("Desktop/REx/text.txt") # => "Desktop/REx"
    
  • join:File#joinは定数FILE::SEPARATOR "/"を使って文字列を連結。すでに/入ってたら削除して連結

  • IO#closeはFileクラスのメソッドではない!

  • openはIOにもFileにもあるメソッド

  • ファイルオープン時のモード一覧:指定無ければ読み込みモード

    "r" 読み込みモード

    "w" 書き込みモード。ファイルが存在していればファイルを空にする。

    "a" 書き込みモード。ファイルが存在していれば、ファイルの末尾から追記する。rewindやseek(0,IO::SEEK_SET)でポインタを先頭などに移動しても書き込みは末尾から行う

    "r+" 読み書き両用モード。ファイルの先頭から読み書きを行う。

    "w+" 読み書き両用モード。ファイルが存在していればファイルを空にする。

    "a+" 読み書き両用モード。ファイルの末尾から読み書きを行う。読み込みは先頭から行うが、rewindやseek(0,IO::SEEK_SET)でポインタを先頭などに移動しても書き込みは末尾から行う

Dir

  • rmdir:空のディレクトリを削除、成功すれば0を返す。空でないなど失敗時はエラー
  • delete:同上
  • chdir:カレントディレクトリを引数のpathに変更する
  • pwd:カレントディレクトリのフルパスを返す
  • getwd:同上
  • home:現在のユーザまたは引数で指定したユーザのホームディレクトリを返す

Time

  • フォーマット文字列 %x # ⇒ 日付(%m/%d/%y)
  • フォーマット文字列 %F # ⇒ 日付(%Y-%m-%d)

Thread

  • new,start,forkでスレッドオブジェクトを生成(ブロック必須、ないとエラー)
  • newのみinitializeを呼び出す

正規表現

  • \w:英数字とアンダースコア、\W:それ以外
  • \d:数字、\D:数字以外
  • .:改行除く任意の1文字、mオプションあれば改行も含む
  • \s:空白文字(\t, \n,\r,\f)、\S:それ以外
  • \A:先頭の文字。改行に影響されない
  • \z:末尾の文字。改行に影響されない、\Z:改行で終わっていればその直前の文字

例外処理

  • raiseで引数を省略すると、RuntimeError
  • rescueで引数を省略すると、StandardErrorを捕捉する

例外クラスの階層

  • Exception
    • ScriptError
      • SyntaxError:文法エラー
    • Signal Exception:補足していないシグナルを受けた
    • StandardError
      • ArgumentError:引数の数が合わない、値が正しくない
      • RuntimeError:特定のクラスに該当しないエラー、例外クラスを省略したraise呼び出し
      • NameError:未定義のローカル変数や定数の参照
        • NoMethodError:未定義のメソッド呼び出し
      • ZeroDivisionError:整数に対し整数の0で除算
  • StandardErrorを継承しないクラスのインスタンス(例えばArrayなど)をraiseメソッドの引数に指定すると、TypeErrorが発生し「exception class/object expected」とメッセージが表示される
  • 複数の例外クラスを補足する方法3選
    • rescue節を複数書く
    • rescueに続けて例外クラスを複数書く
    • rescue *[例外クラス1, 例外クラス2]のように例外クラスの配列をsplat演算子で展開する

問題集で詰まった箇所

  • メソッドの中にProcオブジェクト

    def hoge(step = 1)
        current = 0
        Proc.new {
          current += step
        }
    end
      
    p2 = hoge(2) 
    p p2.class  #=> Proc:hogeメソッドの返り値はProcオブジェクト
    # つまりp2 = Proc.new{current += step}で、currentは最初0、step値として2を渡した
    # p2.callするたびに、step値(今回は2)ずつcurrentに加算されていく
    
  • 文字列の%演算
    sprintfによるフォーマット指定 %<幅>.<精度><指示子>

    sprintf("result: %02d",1)  #=> "result: 01":0で余ったケタは0詰め、2で桁数指定、dで10進数表記
    "result: %02d" % 1  #=> "result: 01":文字列内の%表記に埋め込む数字を外の%の後に表記
    p "Hello%d" % 5  #=> "Hello5"
    
    member = [10, "Tanaka"]
    print "ID:%2d Name:%s"% member  # => ID:10 Name:Tanaka
    # [文字列パターン%配列]で配列の要素を文字列パターン内の%dや%sなどに埋め込んで表示できる
    # 配列から要素が順番に取り出されるので、順番を間違えるとArgumentErrorになる
    
  • 演算子の優先順位

    v1 = 1 - 1 == 0
    v2 = v1 || raise RuntimeError  # ||の優先度が高くv1 || raiseとなり、残った「RuntimeError」が文法エラー
    puts v2 && false
    # 解決策
    v2 = v1 or raise RuntimeError
    v2 = v1 || raise(RuntimeError)
    v2 = v1 || (raise RuntimeError)
    

試験本番で間違えたところ

  • String#delete_suffix!
    メソッドの存在を知りませんでした。

  • Array#delete(val)
    引数に取るのがインデックスか値かど忘れしました…悲

    arr = [1,2,3,1,2,3,1,2,3]
    arr.delete(2)
    p arr # => [1,3,1,3,1,3]
    
  • String#lines
    メソッドが存在するかうろ覚えで間違えました。ブロックを指定するとString#each_lineと同じように動作するそうです。

参考文献、リンク

1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1