Ruby技術者認定試験Silver 2.1 学習用の個人的なメモ　その３

#文字列
##文字列演算
破壊的ruby好き

```str_enzn.rb
a = "ruby"
p a * 5 # --> "rubyrubyrubyrubyruby"
p a     # --> "ruby"

a << " love"
p a     # 破壊的メソッドによる --> "ruby love"
```

##sprintfいろいろ
sprintf と ( の間にスペースがあるとsyntax errorが発生

```sprintf.rb
#sprintf ("%02d",1)   ## sprintf と ( の間にスペースがあると以下のようなエラーとなり動かない
#sprintf.rb:1: syntax error, unexpected ',', expecting ')'

sp02d = sprintf("%02d",1) # --> 01
puts ("#{sp02d}")
sp03d = sprintf("%03d",1) # --> 001
puts ("#{sp03d}")
sp05_2f = sprintf("%05.2f",12.345) # 最後の小数点は四捨五入されます
puts ("#{sp05_2f}")
print ("puts や printは空白あっても問題ないのにー\n")

p sprintf("2進数(15) --> %#b", 15) 
p sprintf("8進数(15) --> %#o", 15)
p sprintf("10進数(15) --> %#d", 15)
p sprintf("16進数(15) --> %#x", 15)
p sprintf("16進数(15) --> %#X", 15) # 大文字に変わるだけ
```

結果

```
% ruby sprintf.rb
01
001
12.35
puts や printは空白あっても問題ないのにー
"2進数(15) --> 0b1111"
"8進数(15) --> 017"
"10進数(15) --> 15"
"16進数(15) --> 0xf"
"16進数(15) --> 0XF"
```

##シンボルと文字列の違い
eql? と equal?の違いもシンボルを使うことでわかりやすい
同じ値であれば eql? は 真となり
同じオブジェクトにならなければ equal? は 真 にならない

```symb.rb
#数値と文字列の比較
test1 = 1234
test2 = "1234"
kekka1 = "OK" if test1.eql?(test2)
print "kekka1 -->"
p kekka1

kekka2 = "OK" if test1.equal?(test2)
print "kekka2 -->"
p kekka2

#文字列と文字列の比較
test1 = "1234"
test2 = "1234"
kekka3 = "OK" if test1.eql?(test2)
print "kekka3 -->"
p kekka3

kekka4 = "OK" if test1.equal?(test2)
print "kekka4 -->"
p kekka4

#シンボルとシンボルの比較
test1 = %s[1234]
test2 = %s[1234]
kekka5 = "OK" if test1.eql?(test2)
print "kekka5 -->"
p kekka5

kekka6 = "OK" if test1.equal?(test2)
print "kekka6 -->"
p kekka6

```

結果

```
ruby symb.rb                                                                                                                                                                                 [23:44:18]
kekka1 -->nil
kekka2 -->nil
kekka3 -->"OK"
kekka4 -->nil
kekka5 -->"OK"
kekka6 -->"OK"
```

#変数と値
変数 = 変数2
の場合、参照渡しとなるが（関数でのパラメータ渡しも同様）
破壊的メソッドで値を書き換えなければ、参照元の値まで書きかわる事はない

```henstai.rb
# 単純な自己代入
def update_object01(a)
    puts "a:[#{a.object_id}]"
    a += "update_object01" # この時点で別のオブジェクトが設定される
    puts "update_object01:[#{a}] object_id:[#{a.object_id}]"
end

# 破壊的メソッド
def update_object02(a)
    puts "a:[#{a.object_id}]"
    a << "update_object02" # この時点で別オブジェクトが設定されずに更新される
    puts "update_object02:[#{a}] object_id:[#{a.object_id}]"
end


def check_object(a, b)
	puts "a:[#{a.object_id}] \nb:[#{b.object_id}]"
	if a.equal?(b)
		puts "同じオブジェクトです"
    else
        puts "別のオブジェクトです"
	end
	puts "a:[#{a}] \nb:[#{b}]"
end

work1 = "ruby"
work2 = "ruby"

# work1 と work2のオブジェクトを確認
puts "work1:[#{work1}] object_id:[#{work1.object_id}]"
puts "work2:[#{work2}] object_id:[#{work2.object_id}]"
check_object work1, work2

# work1のオブジェクトをwork2の参照先へセットする
# 単純な値の代入ではないことが注意
work2 = work1
puts "work1:[#{work1}] object_id:[#{work1.object_id}]"
puts "work2:[#{work2}] object_id:[#{work2.object_id}]"

# work1 と work2のオブジェクトを確認
check_object work1, work2

# 関数呼び出しして値の代入
update_object01(work1)
puts "work1:[#{work1}] object_id:[#{work1.object_id}]"
puts "work2:[#{work2}] object_id:[#{work2.object_id}]"

# 関数呼び出しして破壊的メソッドによる値の設定
update_object02(work1)
puts "work1:[#{work1}] object_id:[#{work1.object_id}]"
puts "work2:[#{work2}] object_id:[#{work2.object_id}]" # work2も値が変わる
```

結果

```
% ruby hensatai.rb
work1:[ruby] object_id:[70115622840640]
work2:[ruby] object_id:[70115622840620]
a:[70115622840640]
b:[70115622840620]
別のオブジェクトです
a:[ruby]
b:[ruby]
work1:[ruby] object_id:[70115622840640]
work2:[ruby] object_id:[70115622840640]
a:[70115622840640]
b:[70115622840640]
同じオブジェクトです
a:[ruby]
b:[ruby]
a:[70115622840640]
update_object01:[rubyupdate_object01] object_id:[70115622839980]
work1:[ruby] object_id:[70115622840640]
work2:[ruby] object_id:[70115622840640]
a:[70115622840640]
update_object02:[rubyupdate_object02] object_id:[70115622840640]
work1:[rubyupdate_object02] object_id:[70115622840640]
work2:[rubyupdate_object02] object_id:[70115622840640]
```

#配列
配列のINDEXに柔軟に値が指定できること
配列の指定した箇所に値を設定できること

```hairetu.rb
# 配列の内容を出力
def p_hairetu(list, label)
	for work in list do
		p "#{label}[value: #{work} class:#{work.class} id:#{work.object_id}]"
	end
end

# 配列の記載の仕方 おそらくこれが一番書きやすい
list = [100,true,"100",100,"200",:test,:test,"101"]
p_hairetu(list, "list")
# インデックス値には計算式が記載できる模様
p "list最初の値 --> #{list[0]} list最後の値 --> #{list[-1]} list[2*2]の値 --> #{list[2*2]}"

# 結果的にはlistと同じ値になる入れ方
list_ = ["a", "b"]
# ０番目から８つの値を書き換える
list_[0, 8] = [100,true,"100",100,"200",:test,:test,"101"]
p_hairetu(list_, "list_")

# 間に挟む事も可能
list_2 = ["a", "b"]
# １番目から値を挿入する
list_2[1, 0] = [100,true,"100",100,"200",:test,:test,"101"]
p_hairetu(list_2, "list_2")

# Arrayを使った方法 初期値指定なし
list2 = Array.new(2) { |i| }
p_hairetu(list2, "list2")

# Arrayを使った方法 初期値をindexと同じ値にする
list3 = Array.new(2) { |i|i }
p_hairetu(list3, "list3")

# Arrayを使った方法 初期値をindex+10とする
list4 = Array.new(2) { |i|i+10  }
p_hairetu(list4, "list4")

# Arrayを使った方法 初期値に文字列を指定する（Arrayで個々に別の文字列を指定する方法はない？）
list5 = Array.new(2) {"test"}
p_hairetu(list5, "list5")


# ２次元配列
list6 = [["0_test1", "0_test2"],["1_test1", "1_test2"]]
p_hairetu(list6, "list6")
```

結果

```
% ruby hairetu.rb
"list[value: 100 class:Fixnum id:201]"
"list[value: true class:TrueClass id:20]"
"list[value: 100 class:String id:70281196529580]"
"list[value: 100 class:Fixnum id:201]"
"list[value: 200 class:String id:70281196529560]"
"list[value: test class:Symbol id:196968]"
"list[value: test class:Symbol id:196968]"
"list[value: 101 class:String id:70281196529540]"
"list最初の値 --> 100 list最後の値 --> 101 list[2*2]の値 --> 200"
"list_[value: 100 class:Fixnum id:201]"
"list_[value: true class:TrueClass id:20]"
"list_[value: 100 class:String id:70281196528040]"
"list_[value: 100 class:Fixnum id:201]"
"list_[value: 200 class:String id:70281196528020]"
"list_[value: test class:Symbol id:196968]"
"list_[value: test class:Symbol id:196968]"
"list_[value: 101 class:String id:70281196528000]"
"list_2[value: a class:String id:70281196551260]"
"list_2[value: 100 class:Fixnum id:201]"
"list_2[value: true class:TrueClass id:20]"
"list_2[value: 100 class:String id:70281196551200]"
"list_2[value: 100 class:Fixnum id:201]"
"list_2[value: 200 class:String id:70281196551180]"
"list_2[value: test class:Symbol id:196968]"
"list_2[value: test class:Symbol id:196968]"
"list_2[value: 101 class:String id:70281196551160]"
"list_2[value: b class:String id:70281196551240]"
"list2[value:  class:NilClass id:8]"
"list2[value:  class:NilClass id:8]"
"list3[value: 0 class:Fixnum id:1]"
"list3[value: 1 class:Fixnum id:3]"
"list4[value: 10 class:Fixnum id:21]"
"list4[value: 11 class:Fixnum id:23]"
"list5[value: test class:String id:70281196548500]"
"list5[value: test class:String id:70281196548480]"
"list6[value: [\"0_test1\", \"0_test2\"] class:Array id:70281196548120]"
"list6[value: [\"1_test1\", \"1_test2\"] class:Array id:70281196548060]"
```

#多重代入
一行で複数の変数に値セット可能

```tajyu.rb
# 多重代入
work1, work2 = "a", "b"
p "work1:#{work1} work2:#{work2}"

# 多重代入足りない場合はnilが入る
work3, work4, work5 = "a", "b"
p "work3:#{work3} work4:#{work4} work5:#{work5}"
unless work5
	p "work5はnil"
end

# 配列を分解してセットすることができる
list = ["a", "b"]
work6, work7 = list
p "list0:#{list[0]}, #{list[0].class}, #{list[0].object_id} #{list.class}"
p "work6:#{work6}, #{work6.class}, #{work6.object_id} work7:#{work7} #{work7.class}"

# 代入元の配列を破壊してみる
list[0] << " is hakai"
# 参照渡しなので破壊される
p work6
```

結果

```
% ruby tajyu.rb
"work1:a work2:b"
"work3:a work4:b work5:"
"work5はnil"
"list0:a, String, 70137424303760 Array"
"work6:a, String, 70137424303760 work7:b String"

```

#可変長引数など
*を一つ指定すると複数の引数が配列として渡される事になり
*を二つ指定するとハッシュで受け取る指定となる
&指定はブロック（複数処理）を引数として渡す事が可能
上記全て定義されていても呼び出し時省略が可能となる

```kahen.rb
# 可変長引数
def kahen01 (a, *b)
	p "kahen01 b --> #{b} #{b.class}"
end
def kahen02 (a, *b, c)
	p "kahen02 b --> #{b} #{b.class}"
end

# オプション引数（ハッシュ）とブロック引数
def kahen03 (a, **b, &c)
	p "kahen03 b --> #{b} #{b.class}"
	if c
		yield
	end
	p "kahen03 end"
end

# *三つというのはありえない --> syntax error
#def kahen03 (a, ***b)
#	p b
#end

kahen01("test1", "test2", "test3", "test4")
kahen02("test1", "test2", "test3", "test4")
kahen02("test1", "test2")
#kahen03("test1", "test2", "test3", "test4") -> ArgumentError
kahen01("test1", key1:"test2", key2:"test3", key3:"test4")
kahen02("test1", key1:"test2", key2:"test3", key3:"test4")
kahen03("test1", key1:"test2", key2:"test3", key3:"test4")
kahen03("test1", key1:"test2", key2:"test3", key3:"test4") do
	p "block.call"
	p "block.call2"
end
```

結果

```
% ruby kahen.rb
"kahen01 b --> [\"test2\", \"test3\", \"test4\"] Array"
"kahen02 b --> [\"test2\", \"test3\"] Array"
"kahen02 b --> [] Array"
"kahen01 b --> [{:key1=>\"test2\", :key2=>\"test3\", :key3=>\"test4\"}] Array"
"kahen02 b --> [] Array"
"kahen03 b --> {:key1=>\"test2\", :key2=>\"test3\", :key3=>\"test4\"} Hash"
"kahen03 end"
"kahen03 b --> {:key1=>\"test2\", :key2=>\"test3\", :key3=>\"test4\"} Hash"
"block.call"
"block.call2"
```


#配列の演算
配列同士で様々な演算が可能
減算すると対象と同じ値が全て消えた結果になる。
join と 文字列を乗算指定するのとで結果が同じになる

```hairetu.rb
work1 = ["test1", "test2", "test3", "test2", "test3", "test4"]
work2 = ["test10", "test2", "test3"]

p work1 & work2   # --> ["test2", "test3"]
p work1 | work2   # --> ["test1", "test2", "test3", "test4", "test10"]
p work1 + work2   # --> ["test1", "test2", "test3", "test2", "test3", "test4", "test10", "test2", "test3"]
p work1 - work2   # --> ["test1", "test4"]
p work1 * 2       # --> ["test1", "test2", "test3", "test2", "test3", "test4", "test1", "test2", "test3", "test2", "test3", "test4"]
p work2 * "-"     # --> "test10-test2-test3"
p work2.join("-") # --> "test10-test2-test3"
```

#forとeach
forはブロックではない式
eachはブロックとなりメソッド扱い、each内の変数は外からアクセスできない（スコープが異なる）

```for_each.rb
work = 0;
# for基本
for i1 in ["10","20","30"] do
	p "for1 i1--> " + i1
	work += 1
	for_val = 100
end

# doの省略可能
for i2 in ["10","20","30"]
	p "for2 i1--> " + i1 #forで定義した変数が参照可能
	p "for2 i2--> " + i2
	work += 1
	for_val +=1
end

# each は doの省略はできない
["10","20","30"].each do |each_i1|
	p "each1 --> " + each_i1
	work +=1
end

# each doの代わりに{} で記載可能 ついでにINDEXを取得する方法も
["10","20","30"].each_with_index { |each_i2, i|
	#p "each1 --> " + each_i1 # NameError
	p "each2 --> #{each_i2} #{i}" # iはINDEXとなる
	work +=1
}

p "#{work}"
p "#{for_val}"
p "#{i1}" # forで定義した変数が参照可能
#p "#{each_i1}" # each内で定義した変数は参照できない--> undefined 
```

#ハッシュ
- 文字列をキーにする場合性能を考えるとシンボルを指定するほうがよい事が多い
- 自作クラスをキーとして指定する場合には hashメソッド eql?メソッドを適切に定義すること

```hash.rb
# ハッシュの定義いろいろ
hash01 = {} # 空のHash
#hash02 = {key1=>"value1", key2=>"value2"}           # NameError
hash03 = {"key1"=>"value1", "key2"=>"value2"}        # キーがただの文字列
hash04 = {:key1=>"value1", :key2=>"value2"}          # キーがシンボル
hash05 = {key1:"value1", key2:"value2"}              # キーがシンボル
hash06 = Hash[:key1, "value1", :key2, "value2"]      # Hashの[]メソッドから定義
hash07 = [[:key1, "value1"], [:key2, "value2"]].to_h #array -> hash
hash08 = Hash.new("default")                         # 空のHashだが、存在しないキーの場合は default値を設定する
hash08["key1"] = "value1"

p hash01
p hash03
p hash04
p hash05
p hash06
p hash07
p hash08
p %! hash01 --> [#{hash01["key3"]}] hash08 --> [#{hash08["key3"]}]! # hash01 は nil hash08 は default

hash08 = {key1:"value1", key2:"value2"}  # Hashを作り直すのでdefaultは消える
p %! hash01 --> [#{hash01["key3"]}] hash08 --> [#{hash08["key3"]}]!

def hash_test(a, b)
	p "hash_test a --> #{a}"
	p "hash_test b --> #{b}"
end

# ハッシュを引数で渡す
hash_test(hash01, hash03)
# ハッシュをそのまま渡す時は {} を付与する（最後の引数であれば{}の省略は可能）
hash_test({"key1"=>"value1", "key2"=>"value2"}, "key11"=>"value11", "key12"=>"value12")
```


結果

```
% ruby hash.rb
{}
{"key1"=>"value1", "key2"=>"value2"}
{:key1=>"value1", :key2=>"value2"}
{:key1=>"value1", :key2=>"value2"}
{:key1=>"value1", :key2=>"value2"}
{:key1=>"value1", :key2=>"value2"}
{"key1"=>"value1"}
" hash01 --> [] hash08 --> [default]"
" hash01 --> [] hash08 --> []"
"hash_test a --> {}"
"hash_test b --> {\"key1\"=>\"value1\", \"key2\"=>\"value2\"}"
"hash_test a --> {\"key1\"=>\"value1\", \"key2\"=>\"value2\"}"
"hash_test b --> {\"key11\"=>\"value11\", \"key12\"=>\"value12\"}"
```