アットウィキロゴ

Rubyのお勉強

トップページ

よーし、面白そうなRubyを勉強してみますか。

環境

  • Ruby1.9.2
    • これって、エンコーディングが変わったらしく、結構あちこちで問題が出てるらしいから、気をつけよう!

Rubyのお勉強メモ

全般

  • ファイルのエンコードは、先頭に記載?(例はWindowsでのSJIS、Ruby1.9.2のデフォルトはUTF-8)
    # encoding: sjis
    
    • うーん、微妙…。
  • 標準出力は、print,puts,pで、ちょっと違う
    • printは、改行なし
    • putsは、自動改行
    • pは、文字列をダブルクォートで囲んで表示?
  • 判定などの()は不要

構文

  • コメント
    # コメントはシャープ
    x = 10 # 行末まで全部コメント。/**/形式はない
    =begin
    ビギン-エンド形式の
    複数行コメントもあり
    この場合は、行頭に書く必要あり
    =end
    
  • 条件分岐
    • if-else-end
      if x == 10 then # thenは省略可能
        puts "x=10"
      elsif x == 20   # elseifじゃないのか…
        puts "x=20"
      else
         puts "x!=10"
      end
      
  • 配列(Array)
    • 基本はこんなん
      nums = [1,2,3,4,5]
      strs = ["a","b,"c"]
      
    • Array.newでもOK。この場合、初期値指定ができる
      a = Array.new
      a = Array.new(5)   # nilで5つ分初期化
      a = Array.new(5,0) # 0で5つ分初期化
      
    • シンプルに文字列を配列に入れるには、%wが便利
      abc = %w(abc edf) # 空白で区切って配列に入るよー。
      
    • splitでもOK
      abc = "abc 123 XYZ".split()
      
    • インデック指定だと、途中を飛ばしてもいいみたい。その場合は、途中はnilで初期化
      names = ["渡辺","田中","佐藤"]
      names[4] = "田辺"
      
  • イテレータ
    • each →これすごいな!
      • 基本はこんなで
        names.each do |name|
          puts name
        end
        
      • 他の短い記述で書いてみる
        names.each do |name| print name end # そのまま1行にしただけ
        names.each { |name| print name } # do,endを{}に変換→{}の場合は1行するのがルールみたい
        
    • whileで書き直す→途中でnilがあるから表示は止まるけど、そこまでは動く
      while name = names.shift do
        print name
      end
      
      • whileの時は、doも省略可能らしい
        names = ["渡辺","田中","佐藤"]
        names[4] = "田辺"
        while name = names.shift
          print name 
        end
        
    • timesメソッドを使うループは、直観的な書き方だなー
      5.times do
        print "hello!\n"
      end
      
  • メソッドはdefで
    def hello
      print "hello DEF\n"
    end
    hello()
    
  • ハッシュ
    • 基本はこんな感じ
      font_table = {:normal => '+0', :small => '-1', :big => '+1'}
      font_table.each do | key, val |
        print "key=[" + key.to_s + "],val=[" + val.to_s + "]\n"
      end
      
    • ハッシュの書き方は、2通り
      h = { 'key1' => 'val1', 'key2' => 'val2' }
      p h
      h = { key1: 'val1', key2: 'val2' } # こっちは1.9から。キーがシンボルになるよー
      p h
      
    • ハッシュからfetchで値をとると、キーが無い場合に例外が出る。デフォルト値を指定もできる
      puts h.fetch(:key3,"キーが無いよー")
      
    • ハッシュを作る時にデフォルト値を設定できる
      h = Hash.new('ハッシュのデフォルト値')
      puts h["no_key"]
      
    • しかも、デフォルト値をブロック処理可能
      h = Hash.new{|hash,key|
        hash[key] = key.upcase
      }
      puts h["no_key"]
      
  • 2次元配列の変わった初期化とか
    • 2次配列の一発初期化
      a = Array.new(5) {|i| i+1 }
      p a
      
    • これだと、同じオブジェクトで初期化されるから注意!
      a = Array.new(3, [0,0,0])
      a[0][1] = 2
      p a
      
    • 2つの配列で、同時に処理も可能。これ、使うかな…。
      ary1 = [1,2,3]
      ary2 = [10,20,30]
      ary3 = [100,200,300]
      result =[]
      ary1.zip(ary2, ary3) do |a,b,c|
        result << a+b+c # <<はpushと同じ
      end
      p result
      
  • 多重代入とか
    • 多重代入は、配列にも適用可能!
      ary = [1,2]
      a,b=ary
      p a
      p b
      
    • 配列から先頭だけ取りたい時は、a,
      a, = ary
      p a
      
    • 関数の引数で、不定引数にも使える
      def foo(arg,*args)
        p arg
        p args
      end
      foo(1)
      foo(1,2,3)
      
    • each_with_indexだと、|key,val|, indexをいっぺんに取れる
      hash = {:a => 100, :b => 200, :c => 300}
      hash.each_with_index { |(key,val),index | p [key,val,index] }
      
  • 文字列系
    • 文字列の長さは、length/size
      p "This is Ruby".length
      p "This is Ruby".size
      
    • 日本語だと、おかしくなるから、正規表現を使うらしいけど、encodingを指定してると、関係なさそう
      p "12345".length
      p "12345".size
      # p "12345".split(//u).length # UTF-8の場合→Ruby1.9の場合は、エラーになるよ!
      
    • 文字列をつなげるには+
      str1 = "最初の言葉"
      str2 = "次の言葉"
      str = str1 + str2
      puts str1,str2,str
      
    • 元の文字に追加するのは、<<でね
      str1 << str2
      puts str1
      
  • クラス
    • 基本はこんな感じ
      class HelloWorld # クラス名は大文字スタート
        def initialize(myname="Ruby") #コンストラクタはinitialize
          @name = myname # @でインスタンス変数
        end
        def hello # インスタンスメソッド。デフォルトはpublic
          print "hello world ", @name , "\n"
        end
        def HelloWorld.hello2(name) #クラスメソッドはnewしなくても使える
          print "hello world2 ", @name , "\n"
        end
        def self.hello3(name) #クラスメソッドはselfでもOK
          print "hello world2 ", @name , "\n"
        end
        class << self # クラスメソッドはこんな風にクラス定義もできる(複数メソッドの時はいいかな)
          def hello3(name)
            print "Hello2! ", name , ".\n"
          end
        end
      end
      bob   = HelloWorld.new("Bob")
      ruby  = HelloWorld.new
      bob.hello
      ruby.hello
      # クラスメソッドはどっちでも呼べる
      HelloWorld.hello2('Name')
      HelloWorld::hello3('Name')
      
    • アクセスメソッドは簡単でいいな!
      class HelloWorld
        attr_accessor :name #これだけで、setter/getter作るのと同じってイカス!
        def greet
          print "I am ", self.name ,"\n" # 自分はselfでアクセス
        end
      end
      ruby  = HelloWorld.new
      ruby.name = "hoge"
      ruby.greet
      
    • クラスの上書き
      class RingArray < Array
        def [](i)
          idx = i % size # レシーバを省略するとselfが自動でつくから、これは[self.size]で割ることになる
          super(idx)
        end
      end
      eto = RingArray[ "子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥" ]
      puts eto[6]
      puts eto[11]
      puts eto[15]
      puts eto[-1] # これが亥になる
      
  • ブロック
    • Rubyの特徴的な、「メソッドに処理を渡す仕組み」かな?
    • 正直、これに関してはうまく説明できる気がしないので、一番判り易かったリンクのみ
    • 最近は、このブロック渡しにインスパイヤされて、いろんな言語で実装されてるみたいだねー
  • モジュール
    • モジュールは、クラスを横断するメソッドを定義して、クラスにincludeして使うらしい(これをMixinと呼ぶだね)
      module MyModule
        #メソッド定義
      end
      class MyClass1
        include MyModule
        # ここでMyMduleのメソッドが使える
      end
      
    • 継承っぽいけど、単一継承じゃないからいっぱいincludeできる。。。
      • でも、これってせっかく単一継承にしてるのにどうなんだろ?
    • Mixinすると、同名メソッドはオーバライドされる
      • 順番は、includeは後勝ちで、その後に親クラスみたい
      • includeじゃなくて、オブジェクトに対してextendを使うと、そのオブジェクトに対してだけMixinできる、のかな?
    • あー、クラスに関係なくオブジェクトに直接関連づいたメソッドを「特異メソッド」って言うんだ。

ファイル出力とか

  • シンプルに引数のファイルの内容を出力
    print "-------シンプル版--------------------------------------------\n"
    print File.read(ARGV[0])
    
  • getsを使って、1行ずつ出力
    print "-------gets版--------------------------------------------\n"
    file = open(ARGV[0])
    while text = file.gets
      print text
    end
    file.close
    

TIPS

  • 良く使う簡素な判定と代入
    var = nil
    name = var || "Ruby" # varがnillなら"Ruby"を代入 →これはイイね!
    p name
    
    • 上記の記述は、以下と全く一緒
      name = "Ruby"
      if var != nil
        name = var
      end
      p name
      
  • 配列参照の場合は&&で楽できそう
    item = ary && ary[0] #aryがnilじゃなければary[0]を代入→これもイイね!
    p item
    
  • 自己代入は、||でもOK
    var ||= 1 # var = var || 1 と同じ
    p var
    
  • ブロックだと、自動で後処理を実行してくれる
    File.open("sample.txt") do |f|
      f.each_line do |line|
        puts line
      end
      # ブロックの後処理として、closeが自動的に行われるらしい
    end
    
  • ソート順を指定するのも、ブロック
    sorted = ary.sort do |a,b|
      a <=> b
    end
    p sorted
    
    • この「<=>」って面白いメソッドだなー
      • 左右のオブジェクトを比較して、「<」ならマイナス、「=」なら0、「>」ならプラスになるらしい!
  • sort_byなら、項目ごとの値の評価を1回だけ実行して、その値を元にソートする
    ary = %w( 1000 200 30 4)
    sorted = ary.sort_by do |item|
      item.length
    end
    p sorted
    

Ruby3.0との差分

久しぶりにRubyの仕事になるので、差分のメモを残しておく

  • キーワード引数の分離 (Ruby 3.0以降)
    • Ruby 1.9.2では末尾のハッシュリテラルは引数として渡せましたが、Ruby 3.0以降ではキーワード引数と位置引数が厳密に分離されました。
  • メソッド定義側がキーワード引数を期待していない場合
    # メソッドの定義: 位置引数(ハッシュ)を1つ取る
    def display_profile(options)
      puts "Name: #{options[:name]}"
      puts "Age: #{options[:age]}"
    end
    
    実行環境 実行コード 結果/挙動
    Ruby 1.9.2 display_profile(name: "Alice", age: 30) 正常に実行される
    Ruby 3.0以降 display_profile(name: "Alice", age: 30) ArgumentError (キーワード引数として解釈されるため)
    Ruby 3.0以降 ハッシュとして明示的に渡す display_profile({name: "Alice", age: 30})
  • メソッド定義側がキーワード引数を期待している場合 # メソッドの定義: キーワード引数を2つ取る
    def display_profile(name:, age:)
      puts "Name: #{name}"
      puts "Age: #{age}"
    end
    
    実行環境 実行コード 結果/挙動
    Ruby 1.9.2 display_profile(name: "Alice", age: 30) ArgumentError (1.9.2ではキーワード引数構文は不完全だったため)
    Ruby 3.0以降 display_profile(name: "Alice", age: 30) 正常に実行される
  • Safe Navigation Operator (&.) (Ruby 2.3以降)
    • メソッドチェーンの途中でレシーバがnilになる可能性がある場合に、煩雑なnilチェックを省略できます。
      実行環境 目的の処理 実行コード 結果
      Ruby 1.9.2 nilチェックあり user.profile.address if user && user.profile nilまたは値
      最新版 Safe Navigation Operator user&.profile&.address nilまたは値
      # user が nil の可能性がある
      user = nil
      
      # Ruby 1.9.2 以前
      # if user && user.profile
      #   address = user.profile.address
      # else
      #   address = nil
      # end
      # puts address #=> nil
      
      # Ruby 2.3 以降
      address = user&.profile&.address
      puts address #=> nil
      
  • パターンマッチング (Ruby 3.0以降)
    • 複雑なデータの構造を分解したり、条件分岐を簡潔に記述できる機能です。
      実行環境 目的の処理 実行コード
      Ruby 1.9.2 複雑なcase/if文 ハッシュキーの有無や値のチェックを多段階で行う
      最新版 パターンマッチング case文と => を使用し、構造を記述する
      data = { user_id: 101, status: :active, role: :admin }
      
      case data
      # status が :active かつ role が :admin の場合
      in { status: :active, role: :admin, **rest }
        puts "管理者としてログインしました。"
      # status が :active かつ role が :user で、user_id が 100 より大きい場合
      in { status: :active, role: :user, user_id: id } if id > 100
        puts "一般ユーザーとしてログインしました。ID: #{id}"
      else
        puts "アクセスできません。"
      end
      #=> 管理者としてログインしました。
      
  • Ractor (Ruby 3.0以降)
    • 並行実行のための新しい並列処理モデルです。オブジェクトの共有を制限することでスレッドセーフ性を高めています。
      実行環境 目的の処理 実行コード
      Ruby 1.9.2 並行処理 Thread.new を使用(グローバルロックにより真の並列性は得にくい)
      最新版 並列処理 Ractor.new を使用(一部のオブジェクトを共有せずに実行)
      # Ractor内で処理する値
      sum = 0
      data = [1, 2, 3, 4, 5] 
      
      # データをRactorに送信する
      r = Ractor.new(data) do |data_in|
        # Ractor内の処理は他のRactorやメインスレッドと並列で実行される
        result = data_in.sum
        Ractor.yield result # 結果を返す
      end
      
      # Ractorの結果を受け取る
      sum = r.take
      puts "合計: #{sum}" #=> 合計: 15
      

-
最終更新:2025年11月25日 08:49