Docker

カリキュラムで出てきたので勉強しようと思いました。

Dockerって何??

データやプログラムを隔離できる

正直え??
って感じでした。

パソコンやサーバーでは複数のプログラムが動いており、wordとかエクセルとか...
同じようにサーバーではApachとかMysqlとか複数のプログラムが同時に動いており、それらのデータを独立した環境に隔離して動かすのがDockerの役目

コンテナ

dockerのイメージとしてパソコンやサーバー上の環境を部屋みやいな感じて分けて、この各部屋にプログラムが入っている。でこの部屋のことをコンテナと言うDocker Engineの上にコンテナを容量の許す限り載せることができる。

イメージ

コンテナを操作するにはDocker Engineを使うがそのときイメージと呼ばれるコンテナの素になるものから作成する。

クラスがイメージで
インスタンスがコンテナみたいな???

DockerにはLinux osが必要になる。 何かしらLinuxが要る。

てかそもそもなんで隔離が必要??

多くのプログラムは単独で動いてるのではなく、何しかしらの実行環境やライブラリー他のプログラムを利用している。

一つのプログラムをアプデしたとき他のプログラムに影響を与えてしまう。

例えばシステムAとシステムBが存在する。
これらのシステムAとBはとあるライブラリーを共有して使っている。

ライブラリーをアプデしてver.5になった場合
システムAはver.4じゃないと動かない。
システムBはver.5じゃないと動かない。

これだとシステムAが動かずに困ってしまう。
これはソフトウェアを使ってる話だが、実行環境やライブラリーやファイルも同じことが言える。
共有しているものを一方の都合で変更すると、他のプログラムに不具合が出る。

システムAとBの同じディレクトリを使う設定になり、バージョンの書き換え合戦になることもある。
なので
システムAはver.4のコンテナを用意
システムBはver.5のコンテナを用意
してあげてお互いが干渉しないようにする必要がある。

他に便利なことは...

  • 通常一つのサーバーマシンには一つのwebサーバーしか載せれないが、コンテナ技術を使えば複数のwebサーバーを運用することができる。

  • コンテナは持ち運びできる。開発環境を整えることができるので、同じチューニングしたコンテナを他の人に配ることもでOSが違う場合でも、dockerさえあれば物理的な環境の違いや、サーバー構成の違いを無視することができる。

実際に手を動かしていきます。

チェリー本 7章 その1

クラス

オブジェクト指向プログラミングでは以下のような用語がよく出てくる。

クラスとは
クラスとは一種のデータ型を示している。
オブジェクトの設計図、オブジェクトのひな形と呼ばれていて
rubyではオブジェクトは必ず何かのクラスに属している。
クラスが同じであれば保持している属性(データー項目)、メソッドは原則として同じになる。

オブジェクト、インスタンス、レシーバーとは

クラスはあくまでも設計図
設計図だけ持ってても仕方ないので車の設計図のように
赤い車、黒い車みたいにオブジェクト指向プログラミングでは
クラスからさまざまなオブジェクトが生成されている。

同じクラスから生成されたオブジェクトは
同じメソッド、同じ属性を持つ(データー項目)が
属性の中で保持されるデータ(名前や数値、色は)
そのオブジェクトによって異なる。

#Alice,Rubyさん, 20さいというユーザーのオブジェクトを作成する。
alice = User.new("Alice", "Ruby", 20)

#Bob,Pythonさん, 30さいというユーザーのオブジェクトを作成する。
bob = User.new("Bob", "Python", 30)

p alice.full_name
=>
"Alice Ruby"
p bob.full_name
=>
"Bob Python"

クラスをもとに作られたデータの塊をオブジェクトという
場合によってはインスタンスとも呼ぶ

以下の文は同じ意味

これはUserクラスのオブジェクト
これはUserクラスのインスタンス

メソッドとの関係性を説明する場合 オブジェクトのことをレシーバーとも呼ぶ

以下のコードがあるとする。

user = User.new("alice", "ruby", 20)
user.first_name

このコードは以下のように説明される。

  1. 二行目でUserオブジェクトのfirst_nameメソッドを呼び出している。
  2. ここでのfirst_nameメソッドのレシーバーはuserになる。
  3. レシーバーを英語で書くとreceverと書く「受け取る人」や「受信者」よ呼ぶ

メソッドとメッセージ

メソッドとはオブジェクトが持つ動作や振る舞いのことをメソッドという
なんらかの処理をひとまとめにして名前をつけて何度も再利用できるようにしたものがメソッド

user = User.new("alice", "ruby", 20)
user.first_name

二行目では「userというレシーバーに対してfirst_nameというメッセージを送っている」というイメージ

状態と属性

状態
オブジェクトが保持する
データのことをオブジェクトの状態もしくはステートと呼ぶ。

属性(アトリビュート,プロパティ)
オブジェクトの状態を外部から取得したりもしくは変更したりできる場合がある。
以下のコードは外部から取得したり、変更したりできる。

class User
#読み書きの許可
  attr_accessor :first_name
  #省略
end
user = User.new("Alice", "Ruby", 20)
user.first_name
=>Alice
user.first_name = "ありす"
user.first_name
=>"ありす”

このようにオブジェクトから取得(またはオブジェクトに設定)できる値のことを属性と呼ぶ
多くの場合は属性の名前は名詞になっている

チェリー本 5章 その3

続・ハッシュ ハッシュのキーには文字列よりシンボルの方が適している。
(文字列がだめってわけじゃない!)
ハッシュのキーにシンボルを使うと次のようになる。

#ハッシュのキーをシンボルにする。
cur = { :japan => "yen", :us => "dollar", :india => "rupee" }

#シンボルを使って取り出す
p cur[:japan]
=>
"yen"

#新しいキーと値の組み合わせを追加する。
cur[:italy] = "euro"
p cur
=>
{:japan=>"yen", :us=>"dollar", :india=>"rupee", :italy=>"euro"}

シンボルがキーになる場合は=>を使わずにシンボル: "値"という記法でハッシュが作成できる。
コロンの位置が左から右になっているので気をつける。

#=>の省略形
cur = { japan: "yen", us: "dollar", india: "rupee" }
#取り出すときは一緒
p cur[:japan]

#値もシンボルの時
cur = { japan: :yen, us: :dollar, india: :rupee }
p cur[:japan]
=>
:yen

チェリー本 5章 その2

シンボル

シンボルを表すクラス
文字列の代わりに用いることもできますが必ずしも文字列と同じ振る舞いをするわけでない。
シンボルは:をつけて名前を定義する。

:シンボルの名前

#シンボル作成例
:apple
:japan
:ruby

シンボルと文字列の違い

ここからシンボルと文字列の違いを記載していく
シンボルはSymbolkクラスのオブジェクトで
文字列はStringクラスのオブジェクト

p :apple.class
p "apple".class

=>
Symbol
String

シンボルはrubyの中では整数として管理されている。
表面上は同じように見えるがその中身は整数
そのため2つの値が同じか調べるときは文字列より高速に処理できる。

#文字列より高速に処理でいる
:apple == :apple
"apple" == "apple"

またシンボルは同じシンボルであれば同じオブジェクトである
という特徴から大量の文字列と大量のシンボルを作成した場合だとシンボルの方がメモリー効率がよくなる。

p :apple.object_id
p :apple.object_id
p :apple.object_id
=>
1042908
1042908
1042908

p "apple".object_id
p "apple".object_id
p "apple".object_id
=>
60
80
100

シンボルでは同じオブジェクトに対して
文字列の場合だとオブジェクトは3つ全て異なる。

シンボルはイミュータブルなオブジェクトです。

ミュータブルとは「変更可能な」という意味で、反対にイミュータブルとは「変更できない」という意味
ミュータブルなオブジェクトは破壊的な変更が適用できるため、場合によっては本来変わってほしくない値まで一緒に変わってしまう恐れがある

シンボルは 何か名前をつけたい。名前なので誰かによって勝手に変更されたら困という時に使える。

#文字列は破壊的な変更が可能
string = "apple"
string.upcase!
p string
=>
"apple"

#シンボルはイミュータブルなので,破壊的な変更ができない
symbol = :apple
symbol.upcase!

シンボルの主な特徴

  • 表面上は文字列っぽい為、プログラマーが理解しやすい
  • 内部的には整数と同じ扱いなのでコンピューターが高速に値を比較できる。
  • 同じシンボルは同じオブジェクトのためメモリーの使用効率がいい。
  • イミュータブルの為、勝手に値を変えられることがない

代表的な使用例として
ハッシュのキーで使われる。
文字列よりハッシュで記載したほうが高速に取り出せる

cur = { "japan" => "yen", "us" => "dollar", "india" => "rupee" }
p cur["japan"]
=>
"yen"

#シンボルで記載した方が処理が早い
cur = { :japan => "yen", :us => "dollar", :india => "rupee" }
p cur[:japan]
=>
"yen"

チェリー本 5章 その1

ハッシュ

本当は第4章をじっくり満遍なくやりたかったのですが
チェリー本を広く短期間で全体の章を触れたかったので
一部飛ばしてきました。笑
必ずまた戻ってきます。
きっと...

rubyで難しく感じたので
取り組んでいこうと思いました。

そもそもハッシュって???
ハッシュはキーと値の組み合わせでデータを管理するオブジェクトのこと。
ハッシュの構文になります。

{}

#キーと値の組み合わせを2つ格納するハッシュ
{"キー1"=>"値1","キー2"=>"値2"}

#国ごとの通貨と単位を格納したハッシュ
{ "japan" => "en", "us" => "dollar", "india" => "rupee" }

#もちろん改行して記載可能
{
  "japan" => "en",
  "us" => "dollar",
  "india" => "rupee",
}

#最後に,がついてもエラーにはならない。

要素の追加、取得、削除

追加

新しいキーと値を追加する時の構文

ハッシュ[キー] = 値

ここでは新しくイタリアの通貨を追加します。

currencies = { "japan" => "en", "us" => "dollar", "india" => "rupee" }
currencies["italy"] = "euro"
p currencies
=>
{"japan"=>"en", "us"=>"dollar", "india"=>"rupee", "italy"=>"euro"}

#またすでに存在していた場合は上書きされる。

currencies["japan"] = ""
p currencies
=>
{"japan"=>"", "us"=>"dollar", "india"=>"rupee", "italy"=>"euro"}

取得

キーを取得する時の構文

ハッシュ[キー]

以下はインドの通貨を取得します。
ハッシュの内部構造上キーと値が大量に格納されていても
指定した値を高速で取り出すことができる。

p currencies["india"]
=>
"rupee"

また存在しないキーを指定するとnilがかえる

削除

deleteメソッドを使うと指定したキーに対応する要素を削除できる。

currencies = { "japan" => "en", "us" => "dollar", "india" => "rupee" }
currencies.delete("japan")
p currencies

=>
{"us"=>"dollar", "india"=>"rupee"}

ハッシュを使った繰り返し処理

eachを使うとキーと値の組み合わせ順で取り出すことができる。
キーと値は格納順に取り出されます。
またブロック引数が2つあることも気をつける。

currencies = { "japan" => "en", "us" => "dollar", "india" => "rupee" }
currencies.each { |key, value|
  puts "#{key}:#{value}"
}

=>
japan:en
us:dollar
india:rupee

チェリー本 4章 その4

範囲オブジェクト

rubyには 1~5まで数値、文字a~eまでのように
範囲を示すオブジェクトが存在する。

範囲オブジェクトは..か...を使って作成する。

最初の値..最後の値(最後の値を含む)
最初の値...最後の値(最後の値を含まない)

範囲オブジェクトは変数に入れず直接include?を呼び出す時は()を使う必要がある。

#記載方法

1..5
1...5
"a".."e"

p ("a"..."e").class
=> Range

..と...の違いはinclude?を使えばわかりやすい

#最初の値..最後の値(最後の値を含む)
p (1..5).include?(3)
p (1..5).include?(5)

#最初の値...最後の値(最後の値を含まない)
p (1..5).include?(3)
p (1...5).include?(5)

範囲オブジェクトの便利な場面

配列に対して添字の代わりに範囲オブジェクトを渡すと
指定した範囲の要素を取得することができる。

a = ["a", "b", "c", "d", "e"]

#2番目、4番目の要素を取得できる。
p a[1..3]
=>
["b", "c", "d"]

シンプルに記載したい時

n以上m以下、n以上m未満の判定をしたい場合>や<とか=>を使うより
範囲オブジェクトを使用した方がシンプルに記載できる。

def liquid?(x)
  0 <= x && x < 100
end
p liquid?(-1)
p liquid?(0)
p liquid?(99)
p liquid?(100)
=>
false
true
true
false


#範囲オブジェクトの場合
def liquid?(y)
  (0...100).include?(y) #シンプルに記載できる
end
p liquid?(-1)
p liquid?(0)
p liquid?(99)
p liquid?(100)
=>
false
true
true
false

case文と組み合わせて使うこともできる

def charge(x)
  case x
  when 1..4
    0
  when 6..12
    300
  when 13..18
    600
  else
    1000
  end
end
p charge(1)
p charge(6)
p charge(13)
p charge(20)

値が連続する配列を作成する時

範囲オブジェクトに対してto_aを呼び出すと値が連続する配列を作成することができる。

p (1..5).to_a
p (1...5).to_a
=>
[1, 2, 3, 4, 5]
[1, 2, 3, 4]

p ("a".."d").to_a
p ("a"..."d").to_a
=>
["a", "b", "c", "d"]
["a", "b", "c"]

#[]のなかに*を記載しても
同じように配列は作れる。

p [*1..6]
=>
[1, 2, 3, 4, 5, 6]

繰り返し処理を行える。

範囲オブジェクトを配列に変換すれば配列として繰り返し処理を行うことができる。

number = (1..10).to_a
p number.inject(0) { |x, y| x += y }

=>55

#配列に変換せずに範囲オブジェクトに対して直接injectメソッドを記載しることも可能
p (1..10).inject(0) { |x, y| x += y }

stepメソッド
値を増やすと間隔を指定できる。

numbers = []

#1..10まで2つ飛ばしで繰り返し処理ができる。
(1..10).step(2) { |n| numbers << n }
p numbers

チェリー本 4章 その3

ブロックを使う配列メソッド

使用頻度が高い
配列メソッド

1.mapメソッド

各要素に対してブロックを評価した結果を新しい配列にして返すメソッド

配列numbersに入っている各数値要素を10倍して新しい
配列numbers_1に代入する式

numbers = [1, 2, 3, 4]
numbers_1 = []

numbers.map { |n| numbers_1 << n * 10 }
p numbers_1
=>
[10, 20, 30, 40]

mapメソッドはブロックの戻り値が配列になる為そのまま新しい変数に入れることができる。
numbers = [1, 2, 3, 4]

numbers_1 = numbers.map { |n| n * 10 }
p numbers_1
=>
[10, 20, 30, 40]

2.select /find_all/rejectメソッド

selectメソッドはブロックを評価してその結果がtrueの要素を集めて配列に返すメソッド
rejectメソッドはselectの逆でtrueの要素を除外する

numbers = [1, 2, 3, 4]

p numbers_1 = numbers.select { |n| n.even? } #numbers配列の偶数になる数値を引き抜く
=>
[2, 4]

numbers = [1, 2, 3, 4]

p numbers_1 = numbers.reject { |n| n.even? }#numbers配列の偶数になる数値を除外する。
=>
[1, 3]

3.findメソッド

ブロックの戻り値がtrueになった最初の要素のみ返す

numbers = [1, 2, 3, 4]
p numbers_1 = numbers.find { |n| n.even? }

4.inject/reduceメソッド

畳み込み演算を行うメソッド

numbers = [1, 2, 3, 4]
num = 0
numbers.each { |n| num += n }
p num

#1~4を順に足していくコード
#これをinjectで書いていくと
numbers = [1, 2, 3, 4]
num = numbers.inject(0) { |x, y| x += y }
p num

=>10

1.まず第一ブロック引数xにinject(0)の0が渡される。num = 0の役目
2.nには配列の一番最初の要素1が入る。配列で渡される普通の挙動
3.x + y  が行われて =>  0 + 1の結果が次のxにはいる。
4.xには1、yには2が渡された結果がxに入るこれの繰り返し

p ((((0 + 1) + 2) + 3) + 4)
と同じ式になる。

#injectメソッドは文字列にも使える。
weeks = ["Mon", "The", "Web", "Thu", "Fri", "Sat"].inject("Sum") { |result, s| result + s }
p weeks

=>
"SumMonTheWebThuFriSat"

&とシンボルを使って簡潔に書く

通常使うブロックの下記かより簡潔に書く方法がある。

#通常のブロックの書き方と
p num = [1, 2, 3, 4, 5, 6].select { |n| n.odd? }

省略記法
p num = [1, 2, 3, 4, 5, 6].select(&:odd?)

ただし省略記法を使える時の条件が存在する。

  • ブロックの引数が1つ
  • ブロックの中で呼び出すメソッドには引数がない。
  • ブロックの中でブロック引数に対してメソッドを一回呼び出す以外の処理がない
["ruby", "java", "perl"].map { |s| s.upcase }