第6回に関しての対話実演例 2

文字列(String)の基本

irb(main):001:0> "abcdefg"      # 文字列を用意する
=> "abcdefg"
irb(main):002:0> s="abcdefg"    # 以下の実験のため 変数 s としておく
=> "abcdefg"
irb(main):003:0> s[0]           # 添え字(何番目)を指定して文字を取り出せる
=> "a"                              # 0番から始まる
irb(main):004:0> s[7]           # 
=> nil
irb(main):005:0> s.length       # 長さを取得する関数
=> 7
irb(main):006:0> s[6]
=> "g"
irb(main):007:0> s[-1]          # 後ろから何番目、というアクセスの方法もある
=> "g"
irb(main):008:0> s[-2]
=> "f"
irb(main):009:0> s[-2].class    # Rubyでは、Stringの中から1文字を取り出しても、
=> String                   # その値は「文字」ではなく「文字列」すなわちString
Cとの差異

長い配列

irb(main):010:0> ("a"*1000000).length   # "a"*10000000 とだけ書くと
=> 1000000          # 出力される文字列のため 画面が大変なことになるので、
                    # 結果に対して.lengthを呼んでおく
irb(main):011:0> (aaa="a"*1000000).length   # 変数に代入しておく
=> 1000000          # 長さ百万の文字列が作られていることが確認できる
irb(main):012:0> aaa[10]        # その中から場所指定で1文字取り出す と
=> "a"                          # そこにある値が返ってくる
irb(main):013:0> a[9999999]         # あ、名前を間違えた
Traceback (most recent call last):  # エラーが返ってくる
        4: from C:/tools/ruby26/bin/irb.cmd:31:in `<main>'
        3: from C:/tools/ruby26/bin/irb.cmd:31:in `load'
        2: from C:/tools/ruby26/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
        1: from (irb):13
NameError (undefined local variable or method `a' for main:Object)
irb(main):014:0> aaa[9999999]   # 百万以上の添字を指定すると、範囲外なので nil
=> nil
irb(main):015:0> aaa[999999]    # これなら値がある
=> "a"

文字列の反転(String#reverseに相当するものを再発明してみる)

資料ページも参照)

irb(main):016:0> s          # あらためて出発点のString s の値を確認
=> "abcdefg"
irb(main):017:0> n=s.length ; (0..n/2).each do |i| s[i],< (0..n/2).each do |i| s[i],s[n-1-i]=s[n-1-i],s[i] end ; s
=> "gfedcba"            # 資料のコード(I)をコピペ入力したもの
irb(main):018:0> s
=> "gfedcba"            # もとの文字列 s が変更されていることがわかる
irb(main):019:0> n          # ここからコードを細かく追跡してみる
=> 7
irb(main):020:0> n/2
=> 3                        # 整数の除算は余りを切り捨てる
irb(main):021:0> n.class
=> Integer                  # (nが整数であることを確認した)
irb(main):022:0> 0..n/2
=> 0..3                     # .. 初めの値と終わりの値を .. でつなぐと、
irb(main):023:0> (0..n/2).class
=> Range                    # Range (範囲 を意味する)クラスのオブジェクト
irb(main):024:0> (0..3).each do |i| puts i end
0
1
2
3
=> 0..3                     # Range#each メソッドは 繰り返しをさせる

データの交換と多重代入

irb(main):029:0> a=10
=> 10
irb(main):030:0> b=99
=> 99                       # a,b 2つの変数の値を交換しようとして、
irb(main):031:0> a=b
=> 99
irb(main):032:0> b=a
=> 99                       # この2つの代入を順に行うと、
irb(main):033:0> a
=> 99                           # aに入っていた値 10 は 消失してしまう
irb(main):034:0> b
=> 99                           # (どちらの変数にも 99が入っている)
irb(main):035:0> a=10       # やりなおし。
=> 10
irb(main):036:0> b              # 最初の状態にしておいて、
=> 99
待避線
irb(main):037:0> c=a ; a=b ; b=c
=> 10           # 退避領域(変数 c)を使った交換手順
irb(main):038:0> a
=> 99
irb(main):039:0> b
=> 10           # これなら交換に成功する
irb(main):040:0> a,b=b,a
=> [10, 99]     # こちらは多重代入による(一気に)交換
irb(main):041:0> a
=> 10
irb(main):042:0> b
=> 99           # Ruby(などの最近の言語)ならこちらが手軽で安心

文字列の基本操作(「文字列の反転」 ⅡとⅢ の考え方に沿って)

irb(main):043:0> String.new     # 空文字列を生成するメソッド(あるにはある)
=> ""                               # でも現実には "" とだけ書けばいい(リテラル)  
irb(main):044:0> String.new(10) # 長さを指定して文字列を生成、、、これではできない
Traceback (most recent call last):
        6: from C:/tools/ruby26/bin/irb.cmd:31:in `<main>'
        5: from C:/tools/ruby26/bin/irb.cmd:31:in `load'
        4: from C:/tools/ruby26/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
        3: from (irb):44
        2: from (irb):44:in `new'
        1: from (irb):44:in `initialize'
TypeError (no implicit conversion of Integer into String)
irb(main):045:0> " "*10         # 代わりに、アスタリスク * を使う
=> "          "                     # 数値に対しては乗算だが、文字列に対しては
irb(main):046:0> x=" "*10           #  メソッド(演算子)の意味が変わってくる
=> "          "
irb(main):047:0> x[5]="a"       # 場所を指定して値(文字)を置き換える    
=> "a"
irb(main):048:0> x                  # 結果の確認
=> "     a    "

irb(main):049:0> r=""           # 「文字列の反転」 Ⅲ の考え方に沿って、
=> ""                               # 空文字列からスタートして、
irb(main):050:0> r[0,0]="h"
=> "h"                              # その先頭に一文字挿入
irb(main):051:0> r
=> "h"
irb(main):052:0> r[0,0]="e"         # を、繰り返す
=> "e"
irb(main):053:0> r[0,0]="l"
=> "l"
irb(main):054:0> r[0,0]="l"
=> "l"
irb(main):055:0> r[0,0]="0"
=> "0"
irb(main):056:0> r                  # 挿入された順に後方に押し出されていき
=> "0lleh"                          # 結果的に反転文字列ができている

単語単位の反転

(解説は資料ページ参照)

irb(main):057:0> s="this is a pen"
=> "this is a pen"              # 文字列を用意して
irb(main):058:0> s.reverse          # String#reverse だと
=> "nep a si siht"                  # 文字単位で反転させることになる
irb(main):059:0> s.split        # そこで使うのが、String#split メソッド
=> ["this", "is", "a", "pen"]       # 単語に分解してくれる(スペースが区切り文字)
irb(main):060:0> s.class            # 時々、こうやって、
=> String                           # データ型を確認しておく
irb(main):061:0> s.split(//)    # String#split は(引数によっては)文字単位で分割する
=> ["t", "h", "i", "s", " ", "i", "s", " ", "a", " ", "p", "e", "n"]
irb(main):062:0> s.split('')        # 空文字列か空のRegexp(//)を渡す
=> ["t", "h", "i", "s", " ", "i", "s", " ", "a", " ", "p", "e", "n"]
irb(main):063:0> //.class
=> Regexp                   # 「正規表現」を表すクラス名(こんど詳しく紹介します) 
irb(main):064:0> s.split        # さて、準備ができたところで、
=> ["this", "is", "a", "pen"]
irb(main):065:0> s.split.reverse    # こうすると単語単位で反転できる
=> ["pen", "a", "is", "this"]
irb(main):066:0> s.split(' ').reverse   # splitに明示的に空白文字を渡してもいい
=> ["pen", "a", "is", "this"]
irb(main):067:0> s.split('').reverse    # 空白文字と空文字列は別物
=> ["n", "e", "p", " ", "a", " ", "s", "i", " ", "s", "i", "h", "t"]
irb(main):068:0> s.split.reverse
=> ["pen", "a", "is", "this"]       # こうして単語単位で反転したものは、
irb(main):069:0> s.split.reverse.class
=> Array                            # まだ配列(Array)の形なので、
irb(main):070:0> s.split.reverse.join   # Array#joinで連結して文字列に戻しましょう。
=> "penaisthis"                         # でもこれじゃ全部くっついてしまう。
irb(main):071:0> $,     # 実は Array#joinに渡す引数のデフォルト値(省略された時の値)は
=> nil                  # nil ということになっていて、連結するときに何も挟まない
irb(main):072:0> s.split.reverse.join(nil)
=> "penaisthis"                     # 明示的に nil を挟んでも同じ
irb(main):073:0> s.split.reverse.join("-----")
=> "pen-----a-----is-----this"      # 文字列を渡すとそれを(1つ1つの)間に挟んでjoin
irb(main):074:0> s.split.reverse.join("/")  # よく使われるパターン
=> "pen/a/is/this"
irb(main):075:0> s.split.reverse.join("_")  # 変数名を生成するときによく使われる
=> "pen_a_is_this"                          # スネークケース
irb(main):076:0> s.split.reverse.join(" ")
=> "pen a is this"                  # 今日の正解はこれ。スペースを挟んでjoin

map の実例(練習課題 e に向けて)

(解説は資料ページ参照)

irb(main):077:0> s.split(//)
=> ["t", "h", "i", "s", " ", "i", "s", " ", "a", " ", "p", "e", "n"]
irb(main):078:0> s.split(//).map{|c|c*3}
=> ["ttt", "hhh", "iii", "sss", "   ", "iii", "sss", "   ", "aaa", "   ", "ppp", "eee", "nnn"]